Browse Source

Merge branch 'TechEmpower:master' into master

Huang ziquan 7 months ago
parent
commit
2bd2c510b2
100 changed files with 2153 additions and 2788 deletions
  1. 133 0
      CODE_OF_CONDUCT.md
  2. 3 1
      Dockerfile
  3. 1 0
      frameworks/C++/libsniper/libs/core
  4. 15 0
      frameworks/C++/poco/README.md
  5. 10 1
      frameworks/C++/poco/benchmark.cpp
  6. 6 6
      frameworks/C++/poco/poco.dockerfile
  7. 0 19
      frameworks/C++/silicon/CMakeLists.txt
  8. 0 8
      frameworks/C++/silicon/README.md
  9. 0 28
      frameworks/C++/silicon/benchmark_config.json
  10. 0 47
      frameworks/C++/silicon/build/symbols.hh
  11. 0 102
      frameworks/C++/silicon/build/techempower.hh
  12. 0 41
      frameworks/C++/silicon/build/techempower_lwan.cc
  13. 0 48
      frameworks/C++/silicon/build/techempower_microhttpd.cc
  14. 0 39
      frameworks/C++/silicon/silicon.dockerfile
  15. 5 4
      frameworks/C++/userver/README.md
  16. 0 24
      frameworks/C++/userver/benchmark_config.json
  17. 0 18
      frameworks/C++/userver/config.toml
  18. 0 29
      frameworks/C++/userver/userver-bare.dockerfile
  19. 3 3
      frameworks/C++/userver/userver.dockerfile
  20. 2 17
      frameworks/C++/userver/userver_benchmark/userver_techempower.cpp
  21. 2 5
      frameworks/C++/userver/userver_configs/static_config.yaml
  22. 0 29
      frameworks/C/duda/README.md
  23. 0 24
      frameworks/C/duda/benchmark_config.json
  24. 0 13
      frameworks/C/duda/duda.dockerfile
  25. 0 7
      frameworks/C/duda/webservice/Makefile.in
  26. 0 81
      frameworks/C/duda/webservice/main.c
  27. 1 0
      frameworks/C/h2o/benchmark_config.json
  28. 28 18
      frameworks/C/h2o/h2o.dockerfile
  29. 6 4
      frameworks/C/h2o/src/database.c
  30. 2 2
      frameworks/C/h2o/src/database.h
  31. 119 62
      frameworks/C/h2o/src/handlers/world.c
  32. 0 17
      frameworks/CSharp/appmpower/appmpower-ado-pg.dockerfile
  33. 53 0
      frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile
  34. 14 4
      frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile
  35. 8 2
      frameworks/CSharp/appmpower/appmpower.dockerfile
  36. 4 4
      frameworks/CSharp/appmpower/benchmark_config.json
  37. 2 2
      frameworks/CSharp/appmpower/config.toml
  38. 10 0
      frameworks/CSharp/appmpower/odbcinst.ini
  39. 0 15
      frameworks/CSharp/appmpower/src/CachedWorldSerializer.cs
  40. 0 200
      frameworks/CSharp/appmpower/src/Data/DbConnection.cs
  41. 0 67
      frameworks/CSharp/appmpower/src/Data/DbConnections.cs
  42. 0 17
      frameworks/CSharp/appmpower/src/Data/DbProviderFactory.cs
  43. 0 16
      frameworks/CSharp/appmpower/src/Data/InternalConnection.cs
  44. 0 117
      frameworks/CSharp/appmpower/src/HttpApplication.cs
  45. 0 7
      frameworks/CSharp/appmpower/src/JsonMessage.cs
  46. 0 58
      frameworks/CSharp/appmpower/src/Kestrel/Json.cs
  47. 0 38
      frameworks/CSharp/appmpower/src/Kestrel/PlainText.cs
  48. 0 25
      frameworks/CSharp/appmpower/src/Kestrel/ServiceProvider.cs
  49. 0 257
      frameworks/CSharp/appmpower/src/Memory/CacheEntry.cs
  50. 0 32
      frameworks/CSharp/appmpower/src/Memory/CacheEntryHelper.cs
  51. 0 62
      frameworks/CSharp/appmpower/src/Memory/CacheEntryState.cs
  52. 0 140
      frameworks/CSharp/appmpower/src/Memory/CacheEntryTokens.cs
  53. 0 520
      frameworks/CSharp/appmpower/src/Memory/MemoryCache.cs
  54. 0 67
      frameworks/CSharp/appmpower/src/Memory/MemoryCacheOptions.cs
  55. 0 24
      frameworks/CSharp/appmpower/src/Microsoft/CachKey.cs
  56. 0 59
      frameworks/CSharp/appmpower/src/Microsoft/StringBuilderCache.cs
  57. 0 42
      frameworks/CSharp/appmpower/src/Program.cs
  58. 10 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Constants.cs
  59. 40 63
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbCommand.cs
  60. 174 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnection.cs
  61. 61 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnections.cs
  62. 61 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnectionsKeyed.cs
  63. 8 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProvider.cs
  64. 21 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProviderFactory.cs
  65. 9 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Data/Dbms.cs
  66. 66 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/DotnetMethods.cs
  67. 4 17
      frameworks/CSharp/appmpower/src/appMpower.Orm/FortunesView.cs
  68. 19 37
      frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/BatchUpdateString.cs
  69. 0 2
      frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/ConcurrentRandom.cs
  70. 54 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/StringBuilderCache.cs
  71. 203 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs
  72. 1 1
      frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/CachedWorld.cs
  73. 1 3
      frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/Fortune.cs
  74. 1 1
      frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/World.cs
  75. 50 131
      frameworks/CSharp/appmpower/src/appMpower.Orm/RawDb.cs
  76. 24 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/FortunesSerializer.cs
  77. 9 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/IJsonSerializer.cs
  78. 4 2
      frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldSerializer.cs
  79. 24 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldsSerizalizer.cs
  80. 42 0
      frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj
  81. 0 55
      frameworks/CSharp/appmpower/src/appMpower.csproj
  82. 7 0
      frameworks/CSharp/appmpower/src/appMpower/JsonMessage.cs
  83. 120 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/CachingMiddelware.cs
  84. 173 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs
  85. 64 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/JsonMiddleware.cs
  86. 62 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleQueriesMiddleware.cs
  87. 62 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleUpdatesMiddleware.cs
  88. 50 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/PlaintextMiddleware.cs
  89. 57 0
      frameworks/CSharp/appmpower/src/appMpower/Middleware/SingleQueryMiddleware.cs
  90. 62 0
      frameworks/CSharp/appmpower/src/appMpower/NativeMethods.cs
  91. 22 0
      frameworks/CSharp/appmpower/src/appMpower/Objects/Fortune.cs
  92. 44 0
      frameworks/CSharp/appmpower/src/appMpower/Program.cs
  93. 1 1
      frameworks/CSharp/appmpower/src/appMpower/Serializers/IJsonSerializer.cs
  94. 3 3
      frameworks/CSharp/appmpower/src/appMpower/Serializers/JsonMessageSerializer.cs
  95. 2 0
      frameworks/CSharp/appmpower/src/appMpower/Slices/Fortunes.cshtml
  96. 10 0
      frameworks/CSharp/appmpower/src/appMpower/Slices/_ViewImports.cshtml
  97. 56 0
      frameworks/CSharp/appmpower/src/appMpower/Startup.cs
  98. 36 0
      frameworks/CSharp/appmpower/src/appMpower/appMpower.csproj
  99. 9 0
      frameworks/CSharp/appmpower/src/appMpower/appsettings.json
  100. 0 0
      frameworks/CSharp/appmpower/src/appMpower/nuget.config

+ 133 - 0
CODE_OF_CONDUCT.md

@@ -0,0 +1,133 @@
+
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual
+identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+  and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the overall
+  community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or advances of
+  any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email address,
+  without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+  professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official email address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
[email protected].
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of
+actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or permanent
+ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the
+community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
+[https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[Mozilla CoC]: https://github.com/mozilla/diversity
+[FAQ]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations

+ 3 - 1
Dockerfile

@@ -18,7 +18,10 @@ RUN apt-get -yqq update && \
       libpq-dev \
       libpq-dev \
       pkg-config \
       pkg-config \
       python3 \
       python3 \
+      python3-colorama \
       python3-dev \
       python3-dev \
+      python3-dnspython \
+      python3-packaging \
       python3-pip \
       python3-pip \
       python3-psutil \
       python3-psutil \
       python3-psycopg2 \
       python3-psycopg2 \
@@ -28,7 +31,6 @@ RUN apt-get -yqq update && \
     # Ubuntu's equivalent packages are too old and/or broken.
     # Ubuntu's equivalent packages are too old and/or broken.
     pip3 install \
     pip3 install \
       --break-system-packages \
       --break-system-packages \
-      colorama==0.3.1 \
       docker==7.0.0 \
       docker==7.0.0 \
       mysqlclient==2.2.4 \
       mysqlclient==2.2.4 \
       pymongo==4.7.2
       pymongo==4.7.2

+ 1 - 0
frameworks/C++/libsniper/libs/core

@@ -0,0 +1 @@
+Subproject commit a792ecfebb02f98bbdd8db232fba69f3f92907b3

+ 15 - 0
frameworks/C++/poco/README.md

@@ -0,0 +1,15 @@
+# POCO C++ Libraries Benchmarking Test
+
+- [POCO Github Repository](https://github.com/pocoproject/poco)
+- [POCO Website](https://pocoproject.org/)
+
+## Software Versions
+
+- [buildpack-deps noble](https://hub.docker.com/_/buildpack-deps)
+- [g++ 14](https://gcc.gnu.org/gcc-14/)
+- [c++17](https://en.cppreference.com/w/cpp/17)
+- [POCO 1.13.1](https://pocoproject.org/releases/poco-1.13.1/poco-1.13.1-all.zip)
+ 
+## Test URLs
+
+- `PLAINTEXT` - [http://127.0.0.1:8080/plaintext](http://127.0.0.1:8080/plaintext) 

+ 10 - 1
frameworks/C++/poco/benchmark.cpp

@@ -6,6 +6,8 @@
 #include <Poco/Net/HTTPServerRequest.h>
 #include <Poco/Net/HTTPServerRequest.h>
 #include <Poco/Net/HTTPServerResponse.h>
 #include <Poco/Net/HTTPServerResponse.h>
 #include <Poco/Util/ServerApplication.h>
 #include <Poco/Util/ServerApplication.h>
+#include <Poco/Timespan.h>
+#include <Poco/Thread.h>
 
 
 #include <iostream>
 #include <iostream>
 #include <string>
 #include <string>
@@ -15,7 +17,9 @@
 #define PLAIN_CONTENT_TYPE   "text/plain"
 #define PLAIN_CONTENT_TYPE   "text/plain"
 #define RES_BODY             "Hello, World!"
 #define RES_BODY             "Hello, World!"
 #define SERVER_NAME          "poco"
 #define SERVER_NAME          "poco"
+#define MAX_CONNECTIONS      16384
 
 
+using namespace Poco;
 using namespace Poco::Net;
 using namespace Poco::Net;
 using namespace Poco::Util;
 using namespace Poco::Util;
 using namespace std;
 using namespace std;
@@ -58,7 +62,12 @@ protected:
         HTTPServerParams* hsp = new HTTPServerParams;
         HTTPServerParams* hsp = new HTTPServerParams;
         hsp->setMaxThreads(stoi(args[1]));
         hsp->setMaxThreads(stoi(args[1]));
         hsp->setKeepAlive(true);
         hsp->setKeepAlive(true);
-        HTTPServer s(new MyRequestHandlerFactory, ServerSocket(stoi(args[0]), 4000), hsp);
+        hsp->setMaxKeepAliveRequests(MAX_CONNECTIONS);
+        hsp->setMaxQueued(MAX_CONNECTIONS);
+        hsp->setThreadPriority(Thread::PRIO_HIGHEST);
+        ServerSocket socket(stoi(args[0]), MAX_CONNECTIONS);
+        socket.setBlocking(false);
+        HTTPServer s(new MyRequestHandlerFactory, socket, hsp);
         s.start();
         s.start();
         waitForTerminationRequest();
         waitForTerminationRequest();
         s.stop();
         s.stop();

+ 6 - 6
frameworks/C++/poco/poco.dockerfile

@@ -1,11 +1,11 @@
-FROM buildpack-deps:xenial
+FROM buildpack-deps:noble
 
 
 RUN apt-get update -yqq && apt-get install -yqq software-properties-common unzip cmake
 RUN apt-get update -yqq && apt-get install -yqq software-properties-common unzip cmake
 
 
-RUN apt-get install -yqq g++-4.8 libjson0-dev
-RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 50
+RUN apt-get install -yqq g++-14
+RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 50
 
 
-ENV POCO_VERSION 1.6.1
+ENV POCO_VERSION 1.13.3
 ENV POCO_HOME /poco
 ENV POCO_HOME /poco
 
 
 WORKDIR ${POCO_HOME}
 WORKDIR ${POCO_HOME}
@@ -20,10 +20,10 @@ ENV LD_LIBRARY_PATH ${POCO_HOME}/lib/Linux/x86_64
 
 
 COPY benchmark.cpp benchmark.cpp
 COPY benchmark.cpp benchmark.cpp
 
 
-RUN g++-4.8 \
+RUN g++-14 \
     -O3 \
     -O3 \
     -DNDEBUG \
     -DNDEBUG \
-    -std=c++0x \
+    -std=c++17 \
     -o \
     -o \
     poco \
     poco \
     benchmark.cpp \
     benchmark.cpp \

+ 0 - 19
frameworks/C++/silicon/CMakeLists.txt

@@ -1,19 +0,0 @@
-cmake_minimum_required(VERSION 2.8)
-
-project(silicon)
-
-include_directories(/include $ENV{MICROHTTPD_HOME}/include)
-
-link_directories(/lib $ENV{MICROHTTPD_HOME}/lib)
-
-add_definitions(-std=c++14 -ftemplate-depth=1024 -DNDEBUG -O3)
-
-add_executable(silicon_tpc_mysql techempower_microhttpd.cc)
-target_link_libraries(silicon_tpc_mysql microhttpd mysqlclient)
-
-add_executable(silicon_epoll_mysql techempower_microhttpd.cc)
-set_target_properties(silicon_epoll_mysql PROPERTIES COMPILE_FLAGS "-DTFB_USE_EPOLL")
-target_link_libraries(silicon_epoll_mysql microhttpd mysqlclient)
-
-add_executable(silicon_lwan_mysql techempower_lwan.cc)
-target_link_libraries(silicon_lwan_mysql mysqlclient lwan ubsan curl z pthread dl luajit-5.1)

+ 0 - 8
frameworks/C++/silicon/README.md

@@ -1,8 +0,0 @@
-# C++/silicon Benchmarking test
-
-Silicon is a C++ web framework located at https://github.com/matt-42/silicon
-
-### Note
-
-The `silicon-epoll-mysql` and `silicon-lwan-mysql` tests are currently not working. They have been removed from the `benchmark_config.json` file but the implementations still exist. You can see the old `benchmark_config.json` [here](https://github.com/TechEmpower/FrameworkBenchmarks/blob/5d44d57cbb5cbc209a2d6aeb23010b466c055200/frameworks/C%2B%2B/silicon/benchmark_config.json).
-

+ 0 - 28
frameworks/C++/silicon/benchmark_config.json

@@ -1,28 +0,0 @@
-{
-  "framework": "silicon",
-  "tests": [{
-    "default": {
-      "json_url"       : "/json",
-      "db_url"         : "/db",
-      "query_url"      : "/queries?queries=",
-      "fortune_url"    : "/fortunes",
-      "update_url"     : "/updates?queries=",
-      "plaintext_url"  : "/plaintext",
-      "port": 8080,
-      "approach": "Realistic",
-      "classification": "Micro",
-      "database": "MySQL",
-      "framework": "silicon",
-      "language": "C++",
-      "flavor": "None",
-      "orm": "Full",
-      "platform": "None",
-      "webserver": "microhttpd",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "silicon-tpc-mysql",
-      "notes": "",
-      "versus": "silicon"
-    }
-  }]
-}

+ 0 - 47
frameworks/C++/silicon/build/symbols.hh

@@ -1,47 +0,0 @@
-// Generated by iod_generate_symbols.
-#include <iod/symbol.hh>
-#ifndef IOD_SYMBOL_db
-#define IOD_SYMBOL_db
-    iod_define_symbol(db)
-#endif
-
-#ifndef IOD_SYMBOL_fortunes
-#define IOD_SYMBOL_fortunes
-    iod_define_symbol(fortunes)
-#endif
-
-#ifndef IOD_SYMBOL_id
-#define IOD_SYMBOL_id
-    iod_define_symbol(id)
-#endif
-
-#ifndef IOD_SYMBOL_json
-#define IOD_SYMBOL_json
-    iod_define_symbol(json)
-#endif
-
-#ifndef IOD_SYMBOL_message
-#define IOD_SYMBOL_message
-    iod_define_symbol(message)
-#endif
-
-#ifndef IOD_SYMBOL_plaintext
-#define IOD_SYMBOL_plaintext
-    iod_define_symbol(plaintext)
-#endif
-
-#ifndef IOD_SYMBOL_queries
-#define IOD_SYMBOL_queries
-    iod_define_symbol(queries)
-#endif
-
-#ifndef IOD_SYMBOL_randomNumber
-#define IOD_SYMBOL_randomNumber
-    iod_define_symbol(randomNumber)
-#endif
-
-#ifndef IOD_SYMBOL_updates
-#define IOD_SYMBOL_updates
-    iod_define_symbol(updates)
-#endif
-

+ 0 - 102
frameworks/C++/silicon/build/techempower.hh

@@ -1,102 +0,0 @@
-#include <unistd.h>
-#include <iostream>
-#include <silicon/api.hh>
-#include <silicon/middleware_factories.hh>
-#include <silicon/middlewares/mysql_connection.hh>
-#include <silicon/middlewares/mysql_orm.hh>
-#include "symbols.hh"
-
-using namespace s;
-using namespace sl;
-
-typedef decltype(D(_id(_auto_increment, _primary_key) = int(),
-                   _randomNumber = int())) random_number;
-
-typedef decltype(D(_id(_auto_increment, _primary_key) = int(),
-                   _message = std::string())) fortune;
-
-typedef mysql_orm_factory<random_number> rn_orm_factory;
-typedef mysql_orm<random_number> rn_orm;
-
-typedef mysql_orm_factory<fortune> fortune_orm_factory;
-typedef mysql_orm<fortune> fortune_orm;
-
-
-std::string escape_html_entities(std::string& data)
-{
-    std::string buffer;
-    buffer.reserve(data.size());
-    for(size_t pos = 0; pos != data.size(); ++pos) {
-        switch(data[pos]) {
-            case '&':  buffer.append("&amp;");       break;
-            case '\"': buffer.append("&quot;");      break;
-            case '\'': buffer.append("&apos;");      break;
-            case '<':  buffer.append("&lt;");        break;
-            case '>':  buffer.append("&gt;");        break;
-            default:   buffer.append(&data[pos], 1); break;
-        }
-    }
-    return std::move(buffer);
-}
-
-auto techempower_api = http_api(
-
-  GET / _plaintext = [] () { return response(_content_type = string_ref("text/plain"),
-                                       _body = string_ref("Hello, World!")); },
-
-  GET / _json = [] () { return response(_content_type = string_ref("application/json"),
-                                  _body = D(_message = "Hello, World!")); },
-                        
-  GET / _db = [] (rn_orm& orm) {
-    random_number r;
-    orm.find_by_id(1245, r);
-    return response(_content_type = "application/json",
-                    _body = r);
-  },
-
-  GET / _queries * get_parameters(_queries = optional(std::string("1"))) = [] (auto param, rn_orm& orm) {
-    int N = atoi(param.queries.c_str());
-    N = std::max(1, std::min(N, 500));
-
-    std::vector<random_number> qs(N);
-    for (int i = 0; i < N; i++)
-      orm.find_by_id(1 + rand() % 9999, qs[i]);
-    return response(_content_type = "application/json",
-                    _body = std::move(qs));
-  },
-
-  GET / _updates * get_parameters(_queries = optional(std::string("1"))) = [] (auto param, rn_orm& orm) {
-    int N = atoi(param.queries.c_str());
-    N = std::max(1, std::min(N, 500));
-
-    std::vector<random_number> qs(N);
-    for (int i = 0; i < N; i++)
-    {
-      orm.find_by_id(1 + rand() % 9999, qs[i]);
-      qs[i].randomNumber = 1 + rand() % 9999;
-      orm.update(qs[i]);
-    }
-    return response(_content_type = "application/json",
-                    _body = std::move(qs));
-  },
-  
-  GET / _fortunes = [] (fortune_orm& orm) {
-    std::vector<fortune> table;
-    orm.forall([&] (fortune& f) { table.push_back(f); });
-    table.push_back(fortune(0, "Additional fortune added at request time."));
-
-    std::sort(table.begin(), table.end(),
-              [] (const fortune& a, const fortune& b) { return a.message < b.message; });
-
-    std::stringstream ss;
-
-    ss << "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>";
-    for(auto& f : table)
-      ss << "<tr><td>" << f.id << "</td><td>" << escape_html_entities(f.message) << "</td></tr>";
-    ss << "</table></body></html>";
-
-    return response(_content_type = "text/html; charset=utf-8",
-                    _body = ss.str());
-  }
-  
-  );

+ 0 - 41
frameworks/C++/silicon/build/techempower_lwan.cc

@@ -1,41 +0,0 @@
-#include <silicon/backends/lwan.hh>
-
-#include "techempower.hh"
-
-using namespace s;
-using namespace sl;
-
-int main(int argc, char* argv[])
-{
-  if (argc != 3)
-  {
-    std::cerr << "Usage: " << argv[0] << " mysql_host port" << std::endl;
-    return 1;
-  }
-
-  auto techempower_middlewares = middleware_factories(
-    mysql_connection_factory(argv[1], "benchmarkdbuser", "benchmarkdbpass", "hello_world"),
-    fortune_orm_factory("Fortune"),
-    rn_orm_factory("World")
-    );
-  
-  try
-  {
-
-    // Write the pid.
-    std::ofstream pidfile(argv[3]);
-    pidfile << getpid() << std::endl;
-    pidfile.close();
-
-    // Start the server.
-    sl::lwan_json_serve(techempower_api, techempower_middlewares, atoi(argv[2]));
-  }
-  catch (std::exception& e)
-  {
-    std::cerr << e.what() << std::endl;
-  }
-  catch (sl::error::error& e)
-  {
-    std::cerr << e.what() << std::endl;
-  }
-}

+ 0 - 48
frameworks/C++/silicon/build/techempower_microhttpd.cc

@@ -1,48 +0,0 @@
-#include <silicon/backends/mhd.hh>
-
-#include "techempower.hh"
-
-using namespace s;
-using namespace sl;
-
-int main(int argc, char* argv[])
-{
-
-  if (argc != 4)
-  {
-    std::cerr << "Usage: " << argv[0] << " mysql_host port nthreads" << std::endl;
-    return 1;
-  }
-
-  auto techempower_middlewares = middleware_factories(
-    mysql_connection_factory(argv[1], "benchmarkdbuser", "benchmarkdbpass", "hello_world"),
-    fortune_orm_factory("Fortune"),
-    rn_orm_factory("World")
-    );
-  
-  try
-  {
-    // Write the pid.
-    std::ofstream pidfile(argv[3]);
-    pidfile << getpid() << std::endl;
-    pidfile.close();
-
-    // Start the server.
-    sl::mhd_json_serve(techempower_api, techempower_middlewares, atoi(argv[2]), _blocking
-#ifdef TFB_USE_EPOLL
-                       , _linux_epoll, _nthreads = atoi(argv[3])
-#else
-                       , _one_thread_per_connection
-#endif
-      );
-    
-  }
-  catch (std::exception& e)
-  {
-    std::cerr << e.what() << std::endl;
-  }
-  catch (sl::error::error& e)
-  {
-    std::cerr << e.what() << std::endl;
-  }
-}

+ 0 - 39
frameworks/C++/silicon/silicon.dockerfile

@@ -1,39 +0,0 @@
-FROM buildpack-deps:xenial
-
-RUN apt-get update -yqq && apt-get install -yqq software-properties-common cmake apt-transport-https
-
-RUN add-apt-repository -s "deb http://apt.llvm.org/`lsb_release -cs`/ llvm-toolchain-`lsb_release -cs`-3.9 main"
-RUN wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add -
-RUN apt-get update -yqq
-RUN apt-get install -yqq clang-3.9 lldb-3.9
-
-ENV MICROHTTPD_VERSION=0.9.39
-ENV MICROHTTPD=/libmicrohttpd
-ENV MICROHTTPD_HOME=$MICROHTTPD-$VERSION
-
-RUN wget http://mirror.ibcp.fr/pub/gnu/libmicrohttpd/libmicrohttpd-$MICROHTTPD_VERSION.tar.gz
-RUN tar xf libmicrohttpd-$MICROHTTPD_VERSION.tar.gz
-RUN cd libmicrohttpd-$MICROHTTPD_VERSION && \
-    ./configure --prefix=$MICROHTTPD_HOME && \
-    make install
-
-ENV PATH=${MICROHTTPD_HOME}/bin:${PATH}
-
-RUN apt-get install -yqq libboost-dev cmake
-
-ENV SILICON=/silicon
-
-COPY ./ ./
-
-RUN git clone https://github.com/matt-42/silicon.git && \
-    cd silicon && \
-    git checkout ecaf04887c9dbbf0f457afab1f487268f6aeffab && \
-    CC=clang-3.9 CXX=clang++-3.9 ./install.sh /
-
-RUN cd build && \
-    cmake .. -DCMAKE_CXX_COMPILER=clang++-3.9 && \
-    make silicon_tpc_mysql
-
-EXPOSE 8080
-
-CMD /build/silicon_tpc_mysql tfb-database 8080 $(nproc)

+ 5 - 4
frameworks/C++/userver/README.md

@@ -2,14 +2,11 @@
 
 
 This is the [userver](https://github.com/userver-framework/userver) portion of a [benchmarking test suite](https://github.com/TechEmpower/FrameworkBenchmarks) comparing a variety of web development platforms.
 This is the [userver](https://github.com/userver-framework/userver) portion of a [benchmarking test suite](https://github.com/TechEmpower/FrameworkBenchmarks) comparing a variety of web development platforms.
 
 
-This benchmarks comes in two configurations: **userver** and **userver-bare**, where both configurations use exactly the same handlers code, but **userver-bare** replaces default http implementation of **userver** with custom one.  
-You see, **userver** being feature-rich framework widely used in production comes with a lot of useful functionality built-in (metrics, dynamic configuring, logging/tracing, congestion control etc...) none of which is of any use in benchmarks; although most of that can be disabled via configs, some parts remain, and these parts aren't free.  
-The aim of **userver-bare** is to explore practical limits of lower-level **userver** functionality when performance is an absolute must, while still being idiomatic userver code.
-
 ### Test Type Implementation Source Code
 ### Test Type Implementation Source Code
 
 
 * [Plaintext](userver_benchmark/controllers/plaintext/handler.cpp)
 * [Plaintext](userver_benchmark/controllers/plaintext/handler.cpp)
 * [Json](userver_benchmark/controllers/json/handler.cpp)
 * [Json](userver_benchmark/controllers/json/handler.cpp)
+* [Fortunes](userver_benchmark/controllers/fortunes/handler.cpp)
 * [Single Database Query](userver_benchmark/controllers/single_query/handler.cpp)
 * [Single Database Query](userver_benchmark/controllers/single_query/handler.cpp)
 * [Multiple Database Queries](userver_benchmark/controllers/multiple_queries/handler.cpp)
 * [Multiple Database Queries](userver_benchmark/controllers/multiple_queries/handler.cpp)
 * [Database Updates](userver_benchmark/controllers/updates/handler.cpp)
 * [Database Updates](userver_benchmark/controllers/updates/handler.cpp)
@@ -24,6 +21,10 @@ http://localhost:8080/plaintext
 
 
 http://localhost:8080/json
 http://localhost:8080/json
 
 
+### Fortunes
+
+http://localhost:8080/fortunes
+
 ### Single Database Query
 ### Single Database Query
 
 
 http://localhost:8080/db
 http://localhost:8080/db

+ 0 - 24
frameworks/C++/userver/benchmark_config.json

@@ -25,30 +25,6 @@
         "display_name": "userver",
         "display_name": "userver",
         "notes": "",
         "notes": "",
         "versus": "None"
         "versus": "None"
-      },
-      "bare": {
-        "json_url": "/json",
-        "plaintext_url": "/plaintext",
-        "db_url": "/db",
-        "query_url": "/queries?queries=",
-        "update_url": "/updates?queries=",
-        "cached_query_url": "/cached-queries?count=",
-	"fortune_url": "/fortunes",
-        "port": 8081,
-        "approach": "Realistic",
-        "classification": "Micro",
-        "database": "postgres",
-        "framework": "userver",
-        "language": "C++",
-        "flavor": "None",
-        "orm": "Micro",
-        "platform": "None",
-        "webserver": "None",
-        "os": "Linux",
-        "database_os": "Linux",
-        "display_name": "userver[bare]",
-        "notes": "",
-        "versus": "None"
       }
       }
     }
     }
   ]
   ]

+ 0 - 18
frameworks/C++/userver/config.toml

@@ -18,21 +18,3 @@ orm = "Micro"
 platform = "None"
 platform = "None"
 webserver = "None"
 webserver = "None"
 versus = "None"
 versus = "None"
-
-[bare]
-urls.plaintext = "/plaintext"
-urls.json = "/json"
-urls.db = "/db"
-urls.query = "/queries?queries="
-urls.update = "/updates?queries="
-urls.cached_query = "/cached-queries?count="
-urls.fortune = "/fortunes"
-approach = "Realistic"
-classification = "Micro"
-database = "Postgres"
-database_os = "Linux"
-os = "Linux"
-orm = "Micro"
-platform = "None"
-webserver = "None"
-versus = "None"

+ 0 - 29
frameworks/C++/userver/userver-bare.dockerfile

@@ -1,29 +0,0 @@
-FROM ghcr.io/userver-framework/ubuntu-userver-build-base:v2 AS builder
-
-RUN apt update && \
-    apt install -y lsb-release wget software-properties-common gnupg && \
-        wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && ./llvm.sh 16
-
-WORKDIR /src
-RUN git clone https://github.com/userver-framework/userver.git && \
-    cd userver && git checkout c2ca5454f0b0e93dd0a2e082904dedda5cda3052
-
-COPY userver_benchmark/ ./
-RUN mkdir build && cd build && \
-    cmake -DUSERVER_IS_THE_ROOT_PROJECT=0 -DUSERVER_FEATURE_CRYPTOPP_BLAKE2=0 \
-          -DUSERVER_FEATURE_UTEST=0 \
-          -DUSERVER_FEATURE_POSTGRESQL=1 \
-          -DUSERVER_FEATURE_ERASE_LOG_WITH_LEVEL=warning \
-          -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native -flto=thin" -DCMAKE_C_FLAGS="-march=native -flto=thin" \
-          -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_C_COMPILER=clang-16 -DUSERVER_USE_LD=lld-16 \
-          -DUSERVER_LTO=0 .. && \
-    make -j $(nproc)
-
-FROM builder AS runner
-WORKDIR /app
-COPY userver_configs/* ./
-COPY --from=builder /src/build/userver_techempower ./
-
-EXPOSE 8081
-CMD ./userver_techempower -c ./static_config.yaml
-

+ 3 - 3
frameworks/C++/userver/userver.dockerfile

@@ -1,4 +1,4 @@
-FROM ghcr.io/userver-framework/ubuntu-userver-build-base:v2 AS builder
+FROM ghcr.io/userver-framework/ubuntu-22.04-userver-pg AS builder
 
 
 RUN apt update && \
 RUN apt update && \
     apt install -y lsb-release wget software-properties-common gnupg && \
     apt install -y lsb-release wget software-properties-common gnupg && \
@@ -6,7 +6,7 @@ RUN apt update && \
 
 
 WORKDIR /src
 WORKDIR /src
 RUN git clone https://github.com/userver-framework/userver.git && \
 RUN git clone https://github.com/userver-framework/userver.git && \
-    cd userver && git checkout c2ca5454f0b0e93dd0a2e082904dedda5cda3052
+    cd userver && git checkout bdd5e1e03921ff378b062f86a189c3cfa3d66332
 
 
 COPY userver_benchmark/ ./
 COPY userver_benchmark/ ./
 RUN mkdir build && cd build && \
 RUN mkdir build && cd build && \
@@ -14,7 +14,7 @@ RUN mkdir build && cd build && \
           -DUSERVER_FEATURE_UTEST=0 \
           -DUSERVER_FEATURE_UTEST=0 \
           -DUSERVER_FEATURE_POSTGRESQL=1 \
           -DUSERVER_FEATURE_POSTGRESQL=1 \
           -DUSERVER_FEATURE_ERASE_LOG_WITH_LEVEL=warning \
           -DUSERVER_FEATURE_ERASE_LOG_WITH_LEVEL=warning \
-          -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native -flto=thin" -DCMAKE_C_FLAGS="-march=native -flto=thin" \
+          -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native" -DCMAKE_C_FLAGS="-march=native" \
           -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_C_COMPILER=clang-16 -DUSERVER_USE_LD=lld-16 \
           -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_C_COMPILER=clang-16 -DUSERVER_USE_LD=lld-16 \
           -DUSERVER_LTO=0 .. && \
           -DUSERVER_LTO=0 .. && \
     make -j $(nproc)
     make -j $(nproc)

+ 2 - 17
frameworks/C++/userver/userver_benchmark/userver_techempower.cpp

@@ -46,20 +46,6 @@ class NoopTracingManager final
       userver::server::http::HttpResponse&) const final {}
       userver::server::http::HttpResponse&) const final {}
 };
 };
 
 
-class MinimalMiddlewarePipelineBuilder final
-    : public userver::server::middlewares::PipelineBuilder {
- public:
-  static constexpr std::string_view kName{
-      "minimal-middleware-pipeline-builder"};
-  using userver::server::middlewares::PipelineBuilder::PipelineBuilder;
-
- private:
-  userver::server::middlewares::MiddlewaresList BuildPipeline(
-      userver::server::middlewares::MiddlewaresList) const override {
-    return {"userver-unknown-exceptions-handling-middleware"};
-  }
-};
-
 int Main(int argc, char* argv[]) {
 int Main(int argc, char* argv[]) {
   auto component_list =
   auto component_list =
       userver::components::MinimalServerComponentList()
       userver::components::MinimalServerComponentList()
@@ -78,10 +64,9 @@ int Main(int argc, char* argv[]) {
           .Append<cached_queries::WorldCacheComponent>()  // cache component
           .Append<cached_queries::WorldCacheComponent>()  // cache component
           .Append<cached_queries::Handler>()
           .Append<cached_queries::Handler>()
           .Append<fortunes::Handler>()
           .Append<fortunes::Handler>()
-          // tracing and metrics tweaks
+          // tracing tweaks
           .Append<NoopTracingManager>()
           .Append<NoopTracingManager>()
-          .Append<MinimalMiddlewarePipelineBuilder>()
-          // bare
+          // bare (not used in the benchmark currently)
           .Append<bare::SimpleRouter>()
           .Append<bare::SimpleRouter>()
           .Append<bare::SimpleServer>();
           .Append<bare::SimpleServer>();
 
 

+ 2 - 5
frameworks/C++/userver/userver_configs/static_config.yaml

@@ -1,8 +1,7 @@
 # yaml
 # yaml
 components_manager:
 components_manager:
     event_thread_pool:
     event_thread_pool:
-        threads: 9
-        dedicated_timer_threads: 1
+        threads: 8
     coro_pool:
     coro_pool:
         initial_size: 10000              # Preallocate 10000 coroutines at startup.
         initial_size: 10000              # Preallocate 10000 coroutines at startup.
         max_size: 300000                 # Do not keep more than 300000 preallocated coroutines.
         max_size: 300000                 # Do not keep more than 300000 preallocated coroutines.
@@ -12,7 +11,7 @@ components_manager:
 
 
         main-task-processor:            # Make a task processor for CPU-bound couroutine tasks.
         main-task-processor:            # Make a task processor for CPU-bound couroutine tasks.
             thread_name: main-worker    # OS will show the threads of this task processor with 'main-worker' prefix.
             thread_name: main-worker    # OS will show the threads of this task processor with 'main-worker' prefix.
-            worker_threads: 46
+            worker_threads: 48
             guess-cpu-limit: true
             guess-cpu-limit: true
 
 
         fs-task-processor:              # Make a separate task processor for filesystem bound tasks.
         fs-task-processor:              # Make a separate task processor for filesystem bound tasks.
@@ -29,7 +28,6 @@ components_manager:
                 handler-defaults:
                 handler-defaults:
                     set_tracing_headers: false
                     set_tracing_headers: false
             server-name: us
             server-name: us
-            middleware-pipeline-builder: minimal-middleware-pipeline-builder
         simple-router:
         simple-router:
         simple-server:
         simple-server:
             port: 8081
             port: 8081
@@ -63,7 +61,6 @@ components_manager:
         noop-tracing-manager:
         noop-tracing-manager:
         tracing-manager-locator:
         tracing-manager-locator:
             component-name: noop-tracing-manager
             component-name: noop-tracing-manager
-        minimal-middleware-pipeline-builder:
 
 
         plaintext-handler:
         plaintext-handler:
             path: /plaintext
             path: /plaintext

+ 0 - 29
frameworks/C/duda/README.md

@@ -1,29 +0,0 @@
-# Duda I/O Benchmarking Test
-
-This is the web service used to benchmark Duda I/O web services framework.
-
-http://duda.io
-
-## Requirements
-
-Just the GNU C Compiler and a Linux system running Kernel version >= 2.6.32
-
-## Tests available
-
-### 1. JSON
-
-URL: /json
-
-### 6. Plain text
-
-URL: /plaintext
-
-## About pending tests
-
-Most of tests that are related to database query are pending and will be available for the next Round.
-
-## Contact
-
-Eduardo Silva <[email protected]>
-
-

+ 0 - 24
frameworks/C/duda/benchmark_config.json

@@ -1,24 +0,0 @@
-{
-  "framework": "duda",
-  "tests": [{
-    "default": {
-      "json_url": "/json",
-      "plaintext_url": "/plaintext",
-      "port": 2001,
-      "approach": "Realistic",
-      "classification": "Platform",
-      "database": "None",
-      "framework": "None",
-      "language": "C",
-      "flavor": "None",
-      "orm": "Raw",
-      "platform": "duda",
-      "webserver": "Monkey",
-      "os": "Linux",
-      "database_os": "Linux",
-      "display_name": "Duda I/O",
-      "notes": "",
-      "versus": "duda"
-    }
-  }]
-}

+ 0 - 13
frameworks/C/duda/duda.dockerfile

@@ -1,13 +0,0 @@
-FROM python:2.7
-
-COPY ./ ./
-# Get v0.31 (no official releases that work 2015-06-25)
-
-RUN git clone https://github.com/monkey/dudac.git
-RUN cd dudac && git checkout 7c3d5b03b09fb4cb5f5e338fff72df2e25e95ef0 && \
-    ./dudac -r && \
-    ./dudac -s
-
-EXPOSE 2001
-
-CMD ["./dudac/dudac", "-w", "webservice", "-p", "2001"]

+ 0 - 7
frameworks/C/duda/webservice/Makefile.in

@@ -1,7 +0,0 @@
-NAME    = ws
-CC      = gcc
-CFLAGS  = -g -Wall -O2
-LDFLAGS =
-DEFS    =
-INCDIR  =
-OBJECTS = main.o

+ 0 - 81
frameworks/C/duda/webservice/main.c

@@ -1,81 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-
-/*
- * Duda I/O Benchmark Tests
- * ========================
- * This web service is made for the performance contest made by
- * TechEmpower, mode details here:
- *
- *     http://www.techempower.com/benchmarks
- *
- * At the moment only Tests 1 & 6 are implemented.
- */
-
-#include "webservice.h"
-#include "packages/json/json.h"
-
-/* Test Macros (Tn) */
-#define JSON_CONTENT_TYPE    "Content-Type: application/json"
-#define PLAIN_CONTENT_TYPE   "Content-Type: text/plain"
-#define T6_BODY              "Hello, World!"
-
-DUDA_REGISTER("Duda I/O Benchmark Test", "WS Bench");
-
-/*
- * Test type 1: JSON serialization
- * ===============================
- * This test use the JSON API object to compose the JSON response
- */
-void cb_json(duda_request_t *dr)
-{
-    char *body;
-    int body_len;
-    json_t *j_root;
-
-    /* Instance the JSON object and compose the content */
-    j_root = json->create_object();
-    json->add_to_object(j_root,
-                        "message",
-                        json->create_string("Hello, World!"));
-
-    /* Format output to string */
-    body = json->print_unformatted_gc(dr, j_root);
-    body_len = strlen(body);
-
-    /* Delete the JSON tree */
-    json->delete(j_root);
-
-    /* Compose the response */
-    response->http_status(dr, 200);
-    response->http_header_n(dr, JSON_CONTENT_TYPE, sizeof(JSON_CONTENT_TYPE) - 1);
-    response->print(dr, body, body_len);
-    response->end(dr, NULL);
-}
-
-
-/*
- * Test type 6: Plaintext
- * ======================
- */
-void cb_plaintext(duda_request_t *dr)
-{
-    response->http_status(dr, 200);
-    response->http_header_n(dr, PLAIN_CONTENT_TYPE, sizeof(PLAIN_CONTENT_TYPE) - 1);
-    response->print(dr, T6_BODY, sizeof(T6_BODY) - 1);
-    response->end(dr, NULL);
-}
-
-int duda_main()
-{
-    /* load packages */
-    duda_load_package(json, "json");
-
-    /* let this web service own the virtual host */
-    conf->service_root();
-
-    /* set callbacks */
-    map->static_add("/json", "cb_json");            /* Test #1 */
-    map->static_add("/plaintext", "cb_plaintext");  /* Test #6 */
-
-    return 0;
-}

+ 1 - 0
frameworks/C/h2o/benchmark_config.json

@@ -1,5 +1,6 @@
 {
 {
   "framework": "h2o",
   "framework": "h2o",
+  "maintainers": ["volyrique"],
   "tests": [{
   "tests": [{
     "default": {
     "default": {
       "json_url": "/json",
       "json_url": "/json",

+ 28 - 18
frameworks/C/h2o/h2o.dockerfile

@@ -6,19 +6,28 @@ FROM "ubuntu:${UBUNTU_VERSION}" AS compile
 
 
 ARG DEBIAN_FRONTEND=noninteractive
 ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get -yqq update && \
 RUN apt-get -yqq update && \
+    apt-get -yqq install \
+      ca-certificates \
+      curl \
+      lsb-release && \
+    install -dm755 /usr/share/postgresql-common/pgdg && \
+    curl --fail -LSso /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \
+      "https://www.postgresql.org/media/keys/ACCC4CF8.asc" && \
+    sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \
+      https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > \
+      /etc/apt/sources.list.d/pgdg.list' && \
+    apt-get -yqq update && \
     apt-get -yqq install \
     apt-get -yqq install \
       autoconf \
       autoconf \
       bison \
       bison \
       cmake \
       cmake \
-      curl \
       flex \
       flex \
       g++ \
       g++ \
       libbpfcc-dev \
       libbpfcc-dev \
       libbrotli-dev \
       libbrotli-dev \
       libcap-dev \
       libcap-dev \
-      libicu-dev \
       libnuma-dev \
       libnuma-dev \
-      libreadline-dev \
+      libpq-dev \
       libssl-dev \
       libssl-dev \
       libtool \
       libtool \
       libuv1-dev \
       libuv1-dev \
@@ -33,7 +42,7 @@ RUN apt-get -yqq update && \
       ruby \
       ruby \
       systemtap-sdt-dev
       systemtap-sdt-dev
 
 
-ARG H2O_VERSION=18b175f71ede08b50d3e5ae8303dacef3ea510fc
+ARG H2O_VERSION=c54c63285b52421da2782f028022647fc2ea3dd1
 
 
 WORKDIR /tmp/h2o-build
 WORKDIR /tmp/h2o-build
 RUN curl -LSs "https://github.com/h2o/h2o/archive/${H2O_VERSION}.tar.gz" | \
 RUN curl -LSs "https://github.com/h2o/h2o/archive/${H2O_VERSION}.tar.gz" | \
@@ -57,18 +66,6 @@ RUN curl -LSs "https://github.com/x86-64/mustache-c/archive/${MUSTACHE_C_REVISIO
     CFLAGS="-flto -march=native -mtune=native -O3" ./autogen.sh && \
     CFLAGS="-flto -march=native -mtune=native -O3" ./autogen.sh && \
     make -j "$(nproc)" install
     make -j "$(nproc)" install
 
 
-ARG POSTGRESQL_VERSION=a37bb7c13995b834095d9d064cad1023a6f99b10
-
-WORKDIR /tmp/postgresql-build
-RUN curl -LSs "https://github.com/postgres/postgres/archive/${POSTGRESQL_VERSION}.tar.gz" | \
-      tar --strip-components=1 -xz && \
-    CFLAGS="-flto -march=native -mtune=native -O3" ./configure \
-      --includedir=/usr/local/include/postgresql \
-      --prefix=/usr/local \
-      --with-ssl=openssl && \
-    make -j "$(nproc)" -C src/include install && \
-    make -j "$(nproc)" -C src/interfaces/libpq install
-
 ARG H2O_APP_PREFIX
 ARG H2O_APP_PREFIX
 WORKDIR /tmp/build
 WORKDIR /tmp/build
 COPY CMakeLists.txt ../
 COPY CMakeLists.txt ../
@@ -85,15 +82,28 @@ RUN cmake \
 
 
 FROM "ubuntu:${UBUNTU_VERSION}"
 FROM "ubuntu:${UBUNTU_VERSION}"
 
 
+ARG POSTGRESQL_VERSION=17
+
 ARG DEBIAN_FRONTEND=noninteractive
 ARG DEBIAN_FRONTEND=noninteractive
 RUN apt-get -yqq update && \
 RUN apt-get -yqq update && \
+    apt-get -yqq install \
+      ca-certificates \
+      curl \
+      lsb-release && \
+    install -dm755 /usr/share/postgresql-common/pgdg && \
+    curl --fail -LSso /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \
+      "https://www.postgresql.org/media/keys/ACCC4CF8.asc" && \
+    sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \
+      https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > \
+      /etc/apt/sources.list.d/pgdg.list' && \
+    apt-get -yqq update && \
     apt-get -yqq install \
     apt-get -yqq install \
       libnuma1 \
       libnuma1 \
-      libyajl2
+      libyajl2 \
+      "postgresql-client-${POSTGRESQL_VERSION}"
 ARG H2O_APP_PREFIX
 ARG H2O_APP_PREFIX
 COPY --from=compile "${H2O_APP_PREFIX}" "${H2O_APP_PREFIX}/"
 COPY --from=compile "${H2O_APP_PREFIX}" "${H2O_APP_PREFIX}/"
 COPY --from=compile /usr/local/lib/libmustache_c.so "${H2O_APP_PREFIX}/lib/"
 COPY --from=compile /usr/local/lib/libmustache_c.so "${H2O_APP_PREFIX}/lib/"
-COPY --from=compile /usr/local/lib/libpq.so.5.17 "${H2O_APP_PREFIX}/lib/libpq.so.5"
 ENV LD_LIBRARY_PATH="${H2O_APP_PREFIX}/lib"
 ENV LD_LIBRARY_PATH="${H2O_APP_PREFIX}/lib"
 EXPOSE 8080
 EXPOSE 8080
 ARG BENCHMARK_ENV
 ARG BENCHMARK_ENV

+ 6 - 4
frameworks/C/h2o/src/database.c

@@ -56,8 +56,8 @@ typedef struct {
 
 
 typedef struct {
 typedef struct {
 	list_t l;
 	list_t l;
-	const char *name;
-	const char *query;
+	char *name;
+	char *query;
 } prepared_statement_t;
 } prepared_statement_t;
 
 
 static h2o_socket_t *create_socket(int sd, h2o_loop_t *loop);
 static h2o_socket_t *create_socket(int sd, h2o_loop_t *loop);
@@ -713,8 +713,8 @@ void add_prepared_statement(const char *name, const char *query, list_t **prepar
 
 
 	memset(p, 0, sizeof(*p));
 	memset(p, 0, sizeof(*p));
 	p->l.next = *prepared_statements;
 	p->l.next = *prepared_statements;
-	p->name = name;
-	p->query = query;
+	p->name = h2o_strdup(NULL, name, SIZE_MAX).base;
+	p->query = h2o_strdup(NULL, query, SIZE_MAX).base;
 	*prepared_statements = &p->l;
 	*prepared_statements = &p->l;
 }
 }
 
 
@@ -791,6 +791,8 @@ void remove_prepared_statements(list_t *prepared_statements)
 			                                                        prepared_statements);
 			                                                        prepared_statements);
 
 
 			prepared_statements = prepared_statements->next;
 			prepared_statements = prepared_statements->next;
+			free(p->name);
+			free(p->query);
 			free(p);
 			free(p);
 		} while (prepared_statements);
 		} while (prepared_statements);
 }
 }

+ 2 - 2
frameworks/C/h2o/src/database.h

@@ -49,10 +49,10 @@ typedef struct db_query_param_t {
 	on_result_t on_result;
 	on_result_t on_result;
 	void (*on_timeout)(struct db_query_param_t *);
 	void (*on_timeout)(struct db_query_param_t *);
 	const char *command;
 	const char *command;
-	const char * const *paramValues;
-	const int *paramLengths;
 	const int *paramFormats;
 	const int *paramFormats;
+	const int *paramLengths;
 	const Oid *paramTypes;
 	const Oid *paramTypes;
+	const char * const *paramValues;
 	size_t nParams;
 	size_t nParams;
 	uint_fast32_t flags;
 	uint_fast32_t flags;
 	int resultFormat;
 	int resultFormat;

+ 119 - 62
frameworks/C/h2o/src/handlers/world.c

@@ -57,17 +57,18 @@
 // MAX_UPDATE_QUERY_LEN must be updated whenever UPDATE_QUERY_BEGIN, UPDATE_QUERY_ELEM,
 // MAX_UPDATE_QUERY_LEN must be updated whenever UPDATE_QUERY_BEGIN, UPDATE_QUERY_ELEM,
 // UPDATE_QUERY_MIDDLE, UPDATE_QUERY_ELEM2, and UPDATE_QUERY_END are changed.
 // UPDATE_QUERY_MIDDLE, UPDATE_QUERY_ELEM2, and UPDATE_QUERY_END are changed.
 #define UPDATE_QUERY_BEGIN "UPDATE " WORLD_TABLE_NAME " SET randomNumber = CASE id "
 #define UPDATE_QUERY_BEGIN "UPDATE " WORLD_TABLE_NAME " SET randomNumber = CASE id "
-#define UPDATE_QUERY_ELEM "WHEN %" PRIu32 " THEN %" PRIu32 " "
-#define UPDATE_QUERY_MIDDLE "ELSE randomNumber END WHERE id IN (%" PRIu32
-#define UPDATE_QUERY_ELEM2 ",%" PRIu32
+#define UPDATE_QUERY_ELEM "WHEN $%zu::integer THEN $%zu::integer "
+#define UPDATE_QUERY_MIDDLE "ELSE randomNumber END WHERE id IN ($1::integer"
+#define UPDATE_QUERY_ELEM2 ",$%zu::integer"
 #define UPDATE_QUERY_END ");"
 #define UPDATE_QUERY_END ");"
 
 
 #define MAX_UPDATE_QUERY_LEN(n) \
 #define MAX_UPDATE_QUERY_LEN(n) \
 	(sizeof(UPDATE_QUERY_BEGIN) + sizeof(UPDATE_QUERY_MIDDLE) + \
 	(sizeof(UPDATE_QUERY_BEGIN) + sizeof(UPDATE_QUERY_MIDDLE) + \
 	 sizeof(UPDATE_QUERY_END) - 1 - sizeof(UPDATE_QUERY_ELEM2) + \
 	 sizeof(UPDATE_QUERY_END) - 1 - sizeof(UPDATE_QUERY_ELEM2) + \
 	 (n) * (sizeof(UPDATE_QUERY_ELEM) - 1 + sizeof(UPDATE_QUERY_ELEM2) - 1 + \
 	 (n) * (sizeof(UPDATE_QUERY_ELEM) - 1 + sizeof(UPDATE_QUERY_ELEM2) - 1 + \
-	        3 * (sizeof(MKSTR(MAX_ID)) - 1) - 3 * (sizeof(PRIu32) - 1) - 3))
+	        3 * sizeof(MKSTR(MAX_QUERIES)) - 3 * (sizeof("%zu") - 1)))
 
 
+#define UPDATE_QUERY_NAME_PREFIX WORLD_TABLE_NAME "Update"
 #define USE_CACHE 2
 #define USE_CACHE 2
 #define WORLD_QUERY "SELECT * FROM " WORLD_TABLE_NAME " WHERE id = $1::integer;"
 #define WORLD_QUERY "SELECT * FROM " WORLD_TABLE_NAME " WHERE id = $1::integer;"
 
 
@@ -236,8 +237,10 @@ static int do_multiple_queries(bool do_update, bool use_cache, h2o_req_t *req)
 
 
 	const size_t num_query = get_query_number(req);
 	const size_t num_query = get_query_number(req);
 
 
-	// MAX_QUERIES is a relatively small number, so assume no overflow in the following
-	// arithmetic operations.
+	// MAX_QUERIES is a relatively small number, say less than or equal to UINT16_MAX, so assume no
+	// unsigned overflow in the following arithmetic operations.
+	static_assert(MAX_QUERIES <= UINT16_MAX,
+	              "potential out-of-bounds memory accesses in the following code");
 	assert(num_query && num_query <= MAX_QUERIES);
 	assert(num_query && num_query <= MAX_QUERIES);
 
 
 	size_t base_size = offsetof(multiple_query_ctx_t, res) + num_query * sizeof(query_result_t);
 	size_t base_size = offsetof(multiple_query_ctx_t, res) + num_query * sizeof(query_result_t);
@@ -259,11 +262,16 @@ static int do_multiple_queries(bool do_update, bool use_cache, h2o_req_t *req)
 	size_t sz = base_size + num_query_in_progress * sizeof(query_param_t);
 	size_t sz = base_size + num_query_in_progress * sizeof(query_param_t);
 
 
 	if (do_update) {
 	if (do_update) {
-		const size_t reuse_size = (num_query_in_progress - 1) * sizeof(query_param_t);
-		const size_t update_query_len = MAX_UPDATE_QUERY_LEN(num_query);
+		size_t s = base_size + sizeof(query_param_t);
 
 
-		if (update_query_len > reuse_size)
-			sz += update_query_len - reuse_size;
+		s = (s + _Alignof(const char *) - 1) / _Alignof(const char *);
+		s = s * _Alignof(const char *) + 2 * num_query * sizeof(const char *);
+		s = (s + _Alignof(int) - 1) / _Alignof(int);
+		s = s * _Alignof(int) + 4 * num_query * sizeof(int);
+		s += sizeof(UPDATE_QUERY_NAME_PREFIX MKSTR(MAX_QUERIES));
+
+		if (s > sz)
+			sz = s;
 	}
 	}
 
 
 	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc(sz);
 	multiple_query_ctx_t * const query_ctx = h2o_mem_alloc(sz);
@@ -332,65 +340,57 @@ static int do_multiple_queries(bool do_update, bool use_cache, h2o_req_t *req)
 
 
 static void do_updates(multiple_query_ctx_t *query_ctx)
 static void do_updates(multiple_query_ctx_t *query_ctx)
 {
 {
-	char *iter = (char *) (query_ctx->query_param + 1);
-	size_t sz = MAX_UPDATE_QUERY_LEN(query_ctx->num_result);
-
-	// Sort the results to avoid database deadlock.
-	qsort(query_ctx->res, query_ctx->num_result, sizeof(*query_ctx->res), compare_items);
-	query_ctx->query_param->param.command = iter;
-	query_ctx->query_param->param.nParams = 0;
-	query_ctx->query_param->param.on_result = on_update_result;
-	query_ctx->query_param->param.paramFormats = NULL;
-	query_ctx->query_param->param.paramLengths = NULL;
-	query_ctx->query_param->param.paramValues = NULL;
-	query_ctx->query_param->param.flags = 0;
+	size_t offset =
+		offsetof(multiple_query_ctx_t, res) + query_ctx->num_result * sizeof(*query_ctx->res);
 
 
-	int c = snprintf(iter, sz, UPDATE_QUERY_BEGIN);
-
-	if ((size_t) c >= sz)
-		goto error;
-
-	iter += c;
-	sz -= c;
-
-	for (size_t i = 0; i < query_ctx->num_result; i++) {
-		query_ctx->res[i].random_number = 1 + get_random_number(MAX_ID,
-		                                                        &query_ctx->ctx->random_seed);
-		c = snprintf(iter,
-		             sz,
-		             UPDATE_QUERY_ELEM,
-		             query_ctx->res[i].id,
-		             query_ctx->res[i].random_number);
+	offset = ((offset + _Alignof(query_param_t) - 1) / _Alignof(query_param_t));
+	offset = offset * _Alignof(query_param_t) + sizeof(query_param_t);
+	offset = (offset + _Alignof(const char *) - 1) / _Alignof(const char *);
+	offset *= _Alignof(const char *);
 
 
-		if ((size_t) c >= sz)
-			goto error;
+	const char ** const paramValues = (const char **) ((char *) query_ctx + offset);
+	const size_t nParams = query_ctx->num_result * 2;
 
 
-		iter += c;
-		sz -= c;
-	}
+	offset += nParams * sizeof(*paramValues);
+	offset = (offset + _Alignof(int) - 1) / _Alignof(int);
+	offset *= _Alignof(int);
 
 
-	c = snprintf(iter, sz, UPDATE_QUERY_MIDDLE, query_ctx->res->id);
+	int * const paramFormats = (int *) ((char *) query_ctx + offset);
+	int * const paramLengths = paramFormats + nParams;
+	char * const command = (char *) (paramLengths + nParams);
+	const size_t command_size =
+		sizeof(UPDATE_QUERY_NAME_PREFIX MKSTR(MAX_QUERIES)) - sizeof(UPDATE_QUERY_NAME_PREFIX) + 1;
+	const int c = snprintf(command + sizeof(UPDATE_QUERY_NAME_PREFIX) - 1,
+	                       command_size,
+	                       "%zu",
+	                       query_ctx->num_result);
 
 
-	if ((size_t) c >= sz)
+	if ((size_t) c >= command_size)
 		goto error;
 		goto error;
 
 
-	iter += c;
-	sz -= c;
-
-	for (size_t i = 1; i < query_ctx->num_result; i++) {
-		c = snprintf(iter, sz, UPDATE_QUERY_ELEM2, query_ctx->res[i].id);
-
-		if ((size_t) c >= sz)
-			goto error;
-
-		iter += c;
-		sz -= c;
-	}
-
-	c = snprintf(iter, sz, UPDATE_QUERY_END);
+	memcpy(command, UPDATE_QUERY_NAME_PREFIX, sizeof(UPDATE_QUERY_NAME_PREFIX) - 1);
+	// Sort the results to avoid database deadlock.
+	qsort(query_ctx->res, query_ctx->num_result, sizeof(*query_ctx->res), compare_items);
 
 
-	if ((size_t) c >= sz)
-		goto error;
+	for (size_t i = 0; i < query_ctx->num_result; i++) {
+		query_ctx->res[i].id = htonl(query_ctx->res[i].id);
+		query_ctx->res[i].random_number =
+			htonl(1 + get_random_number(MAX_ID, &query_ctx->ctx->random_seed));
+		paramFormats[2 * i] = 1;
+		paramFormats[2 * i + 1] = 1;
+		paramLengths[2 * i] = sizeof(query_ctx->res[i].id);
+		paramLengths[2 * i + 1] = sizeof(query_ctx->res[i].random_number);
+		paramValues[2 * i] = (const char *) &query_ctx->res[i].id;
+		paramValues[2 * i + 1] = (const char *) &query_ctx->res[i].random_number;
+	}
+
+	query_ctx->query_param->param.command = command;
+	query_ctx->query_param->param.flags = IS_PREPARED;
+	query_ctx->query_param->param.nParams = nParams;
+	query_ctx->query_param->param.on_result = on_update_result;
+	query_ctx->query_param->param.paramFormats = paramFormats;
+	query_ctx->query_param->param.paramLengths = paramLengths;
+	query_ctx->query_param->param.paramValues = paramValues;
 
 
 	if (execute_database_query(&query_ctx->ctx->request_handler_data.hello_world_db,
 	if (execute_database_query(&query_ctx->ctx->request_handler_data.hello_world_db,
 	                           &query_ctx->query_param->param)) {
 	                           &query_ctx->query_param->param)) {
@@ -726,8 +726,14 @@ static result_return_t on_update_result(db_query_param_t *param, PGresult *resul
 		query_ctx->gen = get_json_generator(&query_ctx->ctx->json_generator,
 		query_ctx->gen = get_json_generator(&query_ctx->ctx->json_generator,
 		                                    &query_ctx->ctx->json_generator_num);
 		                                    &query_ctx->ctx->json_generator_num);
 
 
-		if (query_ctx->gen)
+		if (query_ctx->gen) {
+			for (size_t i = 0; i < query_ctx->num_result; i++) {
+				query_ctx->res[i].id = ntohl(query_ctx->res[i].id);
+				query_ctx->res[i].random_number = ntohl(query_ctx->res[i].random_number);
+			}
+
 			serialize_items(query_ctx->res, query_ctx->num_result, &query_ctx->gen, query_ctx->req);
 			serialize_items(query_ctx->res, query_ctx->num_result, &query_ctx->gen, query_ctx->req);
+		}
 		else
 		else
 			send_error(INTERNAL_SERVER_ERROR, REQ_ERROR, query_ctx->req);
 			send_error(INTERNAL_SERVER_ERROR, REQ_ERROR, query_ctx->req);
 	}
 	}
@@ -887,6 +893,57 @@ void initialize_world_handlers(h2o_hostconf_t *hostconf,
                                h2o_access_log_filehandle_t *log_handle,
                                h2o_access_log_filehandle_t *log_handle,
                                request_handler_data_t *data)
                                request_handler_data_t *data)
 {
 {
+	char name[sizeof(UPDATE_QUERY_NAME_PREFIX MKSTR(MAX_QUERIES))];
+	char query[MAX_UPDATE_QUERY_LEN(MAX_QUERIES)];
+	const size_t name_size = sizeof(name) - sizeof(UPDATE_QUERY_NAME_PREFIX) + 1;
+
+	assert(sizeof(name) >= sizeof(UPDATE_QUERY_NAME_PREFIX));
+	memcpy(name, UPDATE_QUERY_NAME_PREFIX, sizeof(UPDATE_QUERY_NAME_PREFIX) - 1);
+	assert(sizeof(query) >= sizeof(UPDATE_QUERY_BEGIN));
+	memcpy(query, UPDATE_QUERY_BEGIN, sizeof(UPDATE_QUERY_BEGIN) - 1);
+
+	for (size_t i = 0; i < MAX_QUERIES; i++) {
+		char *iter = query + sizeof(UPDATE_QUERY_BEGIN) - 1;
+		size_t sz = sizeof(query) - sizeof(UPDATE_QUERY_BEGIN) + 1;
+		int c = snprintf(name + sizeof(UPDATE_QUERY_NAME_PREFIX) - 1, name_size, "%zu", i + 1);
+
+		if ((size_t) c >= name_size)
+			continue;
+
+		for (size_t j = 0; j <= i; j++) {
+			c = snprintf(iter,
+			             sz,
+			             UPDATE_QUERY_ELEM,
+			             2 * j + 1,
+			             2 * j + 2);
+
+			if ((size_t) c >= sz)
+				continue;
+
+			iter += c;
+			sz -= c;
+		}
+
+		assert(sz >= sizeof(UPDATE_QUERY_MIDDLE));
+		memcpy(iter, UPDATE_QUERY_MIDDLE, sizeof(UPDATE_QUERY_MIDDLE) - 1);
+		iter += sizeof(UPDATE_QUERY_MIDDLE) - 1;
+		sz -= sizeof(UPDATE_QUERY_MIDDLE) - 1;
+
+		for (size_t j = 1; j <= i; j++) {
+			c = snprintf(iter, sz, UPDATE_QUERY_ELEM2, 2 * j + 1);
+
+			if ((size_t) c >= sz)
+				continue;
+
+			iter += c;
+			sz -= c;
+		}
+
+		assert(sz >= sizeof(UPDATE_QUERY_END));
+		memcpy(iter, UPDATE_QUERY_END, sizeof(UPDATE_QUERY_END));
+		add_prepared_statement(name, query, &data->prepared_statements);
+	}
+
 	add_prepared_statement(WORLD_TABLE_NAME, WORLD_QUERY, &data->prepared_statements);
 	add_prepared_statement(WORLD_TABLE_NAME, WORLD_QUERY, &data->prepared_statements);
 	register_request_handler("/cached-worlds", cached_queries, hostconf, log_handle);
 	register_request_handler("/cached-worlds", cached_queries, hostconf, log_handle);
 	register_request_handler("/db", single_query, hostconf, log_handle);
 	register_request_handler("/db", single_query, hostconf, log_handle);

+ 0 - 17
frameworks/CSharp/appmpower/appmpower-ado-pg.dockerfile

@@ -1,17 +0,0 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build
-RUN apt-get update
-RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
-
-WORKDIR /app
-COPY src .
-RUN dotnet publish -c Release -o out /p:Driver=ado
-
-# Construct the actual image that will run
-FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime
-
-WORKDIR /app
-COPY --from=build /app/out ./
-
-EXPOSE 8080
-
-ENTRYPOINT ["./appMpower"]

+ 53 - 0
frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile

@@ -0,0 +1,53 @@
+FROM mcr.microsoft.com/dotnet/sdk:9.0.100 AS build
+RUN apt-get update
+RUN apt-get -yqq install clang zlib1g-dev
+RUN apt-get update
+
+WORKDIR /app
+COPY src .
+RUN dotnet publish -c Release -o out /p:Database=mysql
+
+# Construct the actual image that will run
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.0 AS runtime
+
+RUN apt-get update
+# The following installs standard versions unixodbc and pgsqlodbc
+# unixodbc still needs to be installed even if compiled locally
+RUN apt-get install -y unixodbc-dev unixodbc wget curl
+RUN apt-get update
+
+WORKDIR /odbc
+
+RUN curl -L -o mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz https://downloads.mariadb.com/Connectors/odbc/connector-odbc-3.1.20/mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz
+RUN tar -xvzf mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz
+RUN cp mariadb-connector-odbc-3.1.20-debian-bookworm-amd64/lib/mariadb/libm* /usr/lib/
+RUN cp -r /odbc/mariadb-connector-odbc-3.1.20-debian-bookworm-amd64/lib/mariadb /usr/local/lib/mariadb
+RUN rm mariadb-connector-odbc-3.1.20-debian-bookworm-amd64.tar.gz
+#TODOLOCAL
+#RUN curl -L -o mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz https://downloads.mariadb.com/Connectors/odbc/connector-odbc-3.1.20/mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz
+#RUN tar -xvzf mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz
+#RUN cp mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64/lib/mariadb/libm* /usr/lib/
+#RUN cp -r /odbc/mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64/lib/mariadb /usr/local/lib/mariadb
+#RUN rm mariadb-connector-odbc-3.1.20-debian-bookworm-aarch64.tar.gz
+
+ENV PATH=/usr/local/unixODBC/bin:$PATH
+
+WORKDIR /etc/
+COPY odbcinst.ini .
+
+# Full PGO
+ENV DOTNET_TieredPGO 1 
+ENV DOTNET_TC_QuickJitForLoops 1 
+ENV DOTNET_ReadyToRun 0
+
+ENV ASPNETCORE_URLS http://+:8080
+WORKDIR /app
+COPY --from=build /app/out ./
+
+RUN cp /usr/lib/libm* /app
+#RUN cp /usr/lib/aarch64-linux-gnu/libodbc* /app
+RUN cp /usr/lib/x86_64-linux-gnu/libodbc* /app
+
+EXPOSE 8080
+
+ENTRYPOINT ["./appMpower"]

+ 14 - 4
frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile

@@ -1,16 +1,16 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build
+FROM mcr.microsoft.com/dotnet/sdk:9.0.100 AS build
 RUN apt-get update
 RUN apt-get update
 RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
 RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
 
 
 WORKDIR /app
 WORKDIR /app
 COPY src .
 COPY src .
-RUN dotnet publish -c Release -o out /p:Driver=odbc
+RUN dotnet publish -c Release -o out /p:Database=postgresql
 
 
 # Construct the actual image that will run
 # Construct the actual image that will run
-FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.0 AS runtime
 
 
 RUN apt-get update
 RUN apt-get update
-RUN apt-get install -y unixodbc odbc-postgresql
+RUN apt-get install -y unixodbc-dev unixodbc odbc-postgresql
 # unixodbc still needs to be installed even if compiled locally
 # unixodbc still needs to be installed even if compiled locally
 
 
 ENV PATH=/usr/local/unixODBC/bin:$PATH
 ENV PATH=/usr/local/unixODBC/bin:$PATH
@@ -18,9 +18,19 @@ ENV PATH=/usr/local/unixODBC/bin:$PATH
 WORKDIR /etc/
 WORKDIR /etc/
 COPY odbcinst.ini .
 COPY odbcinst.ini .
 
 
+# Full PGO
+ENV DOTNET_TieredPGO 1 
+ENV DOTNET_TC_QuickJitForLoops 1 
+ENV DOTNET_ReadyToRun 0
+
+ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 
 
+#RUN cp /usr/lib/aarch64-linux-gnu/libodbc* /app
+RUN cp /usr/lib/x86_64-linux-gnu/libodbc* /app
+
+
 EXPOSE 8080
 EXPOSE 8080
 
 
 ENTRYPOINT ["./appMpower"]
 ENTRYPOINT ["./appMpower"]

+ 8 - 2
frameworks/CSharp/appmpower/appmpower.dockerfile

@@ -1,14 +1,20 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build
+FROM mcr.microsoft.com/dotnet/sdk:9.0.100 AS build
 RUN apt-get update
 RUN apt-get update
 RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
 RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
 
 
 WORKDIR /app
 WORKDIR /app
 COPY src .
 COPY src .
+#RUN dotnet publish appMpower/appMpower.csproj -c Release -o out
 RUN dotnet publish -c Release -o out
 RUN dotnet publish -c Release -o out
 
 
 # Construct the actual image that will run
 # Construct the actual image that will run
-FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime
+FROM mcr.microsoft.com/dotnet/aspnet:9.0.0 AS runtime
+# Full PGO
+ENV DOTNET_TieredPGO 1 
+ENV DOTNET_TC_QuickJitForLoops 1 
+ENV DOTNET_ReadyToRun 0
 
 
+ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 
 

+ 4 - 4
frameworks/CSharp/appmpower/benchmark_config.json

@@ -39,11 +39,11 @@
         "webserver": "Kestrel",
         "webserver": "Kestrel",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "appMpower [aot-no-reflection,odbc]",
+        "display_name": "appMpower [aot-no-reflection,pg,odbc]",
         "notes": "",
         "notes": "",
         "versus": "aspnetcore-minimal"
         "versus": "aspnetcore-minimal"
       },
       },
-      "ado-pg": {
+      "odbc-my": {
         "db_url": "/db",
         "db_url": "/db",
         "query_url": "/queries?c=",
         "query_url": "/queries?c=",
         "update_url": "/updates?c=",
         "update_url": "/updates?c=",
@@ -52,7 +52,7 @@
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
         "classification": "Platform",
         "classification": "Platform",
-        "database": "Postgres",
+        "database": "MySQL",
         "framework": "appmpower",
         "framework": "appmpower",
         "language": "C#",
         "language": "C#",
         "orm": "Raw",
         "orm": "Raw",
@@ -61,7 +61,7 @@
         "webserver": "Kestrel",
         "webserver": "Kestrel",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "appMpower [aot-no-reflection,ado]",
+        "display_name": "appMpower [aot-no-reflection,my,odbc]",
         "notes": "",
         "notes": "",
         "versus": "aspnetcore-minimal"
         "versus": "aspnetcore-minimal"
       }
       }

+ 2 - 2
frameworks/CSharp/appmpower/config.toml

@@ -30,7 +30,7 @@ platform = ".NET"
 webserver = "Kestrel"
 webserver = "Kestrel"
 versus = "aspnetcore-minimal"
 versus = "aspnetcore-minimal"
 
 
-[ado-pg]
+[odbc-my]
 urls.db = "/db"
 urls.db = "/db"
 urls.query = "/queries?c="
 urls.query = "/queries?c="
 urls.update = "/updates?c="
 urls.update = "/updates?c="
@@ -38,7 +38,7 @@ urls.fortune = "/fortunes"
 urls.cached_query = "/cached-worlds?c="
 urls.cached_query = "/cached-worlds?c="
 approach = "Realistic"
 approach = "Realistic"
 classification = "Micro"
 classification = "Micro"
-database = "Postgres"
+database = "MySQL"
 database_os = "Linux"
 database_os = "Linux"
 os = "Linux"
 os = "Linux"
 orm = "Raw"
 orm = "Raw"

+ 10 - 0
frameworks/CSharp/appmpower/odbcinst.ini

@@ -5,6 +5,7 @@ Pooling=0
 
 
 [ODBC Drivers]
 [ODBC Drivers]
 PostgreSQL = Installed
 PostgreSQL = Installed
+MariaDB = Installed
 
 
 ;
 ;
 ;  odbcinst.ini
 ;  odbcinst.ini
@@ -15,7 +16,16 @@ Description=ODBC for PostgreSQL
 ; in version 08.x. Note that the library can also be installed under an other
 ; in version 08.x. Note that the library can also be installed under an other
 ; path than /usr/local/lib/ following your installation.
 ; path than /usr/local/lib/ following your installation.
 ; This is the standard location used by apt-get install -y unixodbc
 ; This is the standard location used by apt-get install -y unixodbc
+;ON SERVER
 Driver = /usr/lib/x86_64-linux-gnu/odbc/psqlodbcw.so
 Driver = /usr/lib/x86_64-linux-gnu/odbc/psqlodbcw.so
+;TODOLOCAL: ON MAC
+;Driver =/usr/lib/aarch64-linux-gnu/odbc/psqlodbcw.so
+
 ;Driver =/usr/local/pgsqlodbc/lib/psqlodbcw.so
 ;Driver =/usr/local/pgsqlodbc/lib/psqlodbcw.so
 Threading = 0
 Threading = 0
 CPTimeout = 0
 CPTimeout = 0
+
+[MariaDB]
+Description=MariaDB ODBC for MySQL
+Driver = /usr/lib/libmaodbc.so
+Threading   = 0

+ 0 - 15
frameworks/CSharp/appmpower/src/CachedWorldSerializer.cs

@@ -1,15 +0,0 @@
-using System.Text.Json;
-
-namespace appMpower
-{
-   public class CachedWorldSerializer : Kestrel.IJsonSerializer<CachedWorld>
-   {
-      public void Serialize(Utf8JsonWriter utf8JsonWriter, CachedWorld world)
-      {
-         utf8JsonWriter.WriteStartObject();
-         utf8JsonWriter.WriteNumber("id", world.Id);
-         utf8JsonWriter.WriteNumber("randomNumber", world.RandomNumber);
-         utf8JsonWriter.WriteEndObject();
-      }
-   }
-}

+ 0 - 200
frameworks/CSharp/appmpower/src/Data/DbConnection.cs

@@ -1,200 +0,0 @@
-using System.Collections.Concurrent;
-using System.Data;
-using System.Threading.Tasks;
-
-namespace appMpower.Data
-{
-   public class DbConnection : IDbConnection
-   {
-      private string _connectionString;
-      internal InternalConnection _internalConnection;
-
-      public DbConnection()
-      {
-         _connectionString = DbProviderFactory.ConnectionString;
-      }
-
-      public DbConnection(string connectionString)
-      {
-         _connectionString = connectionString;
-      }
-
-      internal ConcurrentDictionary<string, DbCommand> DbCommands
-      {
-         get
-         {
-            return _internalConnection.DbCommands;
-         }
-         set
-         {
-            _internalConnection.DbCommands = value;
-         }
-      }
-
-      public short Number
-      {
-         get
-         {
-            return _internalConnection.Number;
-         }
-         set
-         {
-            _internalConnection.Number = value;
-         }
-      }
-
-      public IDbConnection Connection
-      {
-         get
-         {
-            return _internalConnection.DbConnection;
-         }
-         set
-         {
-            _internalConnection.DbConnection = value;
-         }
-      }
-
-      public string ConnectionString
-      {
-         get
-         {
-            return _internalConnection.DbConnection.ConnectionString;
-         }
-         set
-         {
-            _internalConnection.DbConnection.ConnectionString = value;
-         }
-      }
-
-      public int ConnectionTimeout
-      {
-         get
-         {
-            return _internalConnection.DbConnection.ConnectionTimeout;
-         }
-      }
-
-      public string Database
-      {
-         get
-         {
-            return _internalConnection.DbConnection.Database;
-         }
-      }
-
-      public ConnectionState State
-      {
-         get
-         {
-            if (_internalConnection is null) return ConnectionState.Closed;
-            return _internalConnection.DbConnection.State;
-         }
-      }
-
-      public IDbTransaction BeginTransaction()
-      {
-         return _internalConnection.DbConnection.BeginTransaction();
-      }
-
-      public IDbTransaction BeginTransaction(IsolationLevel il)
-      {
-         return _internalConnection.DbConnection.BeginTransaction(il);
-      }
-
-      public void ChangeDatabase(string databaseName)
-      {
-         _internalConnection.DbConnection.ChangeDatabase(databaseName);
-      }
-
-      public void Close()
-      {
-         _internalConnection.DbConnection.Close();
-      }
-
-      public async Task CloseAsync()
-      {
-         await (_internalConnection.DbConnection as System.Data.Common.DbConnection).CloseAsync();
-      }
-
-      public IDbCommand CreateCommand()
-      {
-         return _internalConnection.DbConnection.CreateCommand();
-      }
-
-      public void Open()
-      {
-         if (_internalConnection.DbConnection.State == ConnectionState.Closed)
-         {
-            _internalConnection.DbConnection.Open();
-         }
-      }
-
-      public void Dispose()
-      {
-#if ADO
-         _internalConnection.DbConnection.Dispose();
-         _internalConnection.Dispose();
-#else
-         DbConnections.Release(_internalConnection);
-#endif
-      }
-
-      public async Task OpenAsync()
-      {
-#if ADO && POSTGRESQL
-         _internalConnection = new(); 
-         _internalConnection.DbConnection = new Npgsql.NpgsqlConnection(_connectionString);
-#else
-         if (_internalConnection is null)
-         {
-            _internalConnection = await DbConnections.GetConnection(_connectionString);
-         }
-#endif
-
-         if (_internalConnection.DbConnection.State == ConnectionState.Closed)
-         {
-            await (_internalConnection.DbConnection as System.Data.Common.DbConnection).OpenAsync();
-         }
-      }
-
-      internal DbCommand GetCommand(string commandText, CommandType commandType, DbCommand dbCommand)
-      {
-#if ADO
-         dbCommand.Command = _internalConnection.DbConnection.CreateCommand();
-         dbCommand.Command.CommandText = commandText;
-         dbCommand.Command.CommandType = commandType;
-         dbCommand.DbConnection = this;
-#else
-         DbCommand internalCommand;
-
-         if (_internalConnection.DbCommands.TryRemove(commandText, out internalCommand))
-         {
-            dbCommand.Command = internalCommand.Command;
-            dbCommand.DbConnection = internalCommand.DbConnection;
-         }
-         else
-         {
-            dbCommand.Command = _internalConnection.DbConnection.CreateCommand();
-            dbCommand.Command.CommandText = commandText;
-            dbCommand.Command.CommandType = commandType;
-            dbCommand.DbConnection = this;
-
-            //For non odbc drivers like Npgsql which do not support Prepare
-            dbCommand.Command.Prepare();
-
-            //Console.WriteLine("prepare pool connection: " + this._internalConnection.Number + " for command " + _internalConnection.DbCommands.Count);
-         }
-#endif
-
-         return dbCommand;
-      }
-
-      public void ReleaseCommand(DbCommand dbCommand)
-      {
-#if !ADO
-         _internalConnection.DbCommands.TryAdd(dbCommand.CommandText, dbCommand);
-#endif
-      }
-   }
-}

+ 0 - 67
frameworks/CSharp/appmpower/src/Data/DbConnections.cs

@@ -1,67 +0,0 @@
-using System.Collections.Concurrent;
-using System.Threading.Tasks;
-
-namespace appMpower.Data
-{
-   public static class DbConnections
-   {
-      private static bool _connectionsCreated = false;
-      private static short _createdConnections = 0;
-      private static short _maxConnections = 500;
-
-      private static ConcurrentStack<InternalConnection> _stack = new();
-      private static ConcurrentQueue<TaskCompletionSource<InternalConnection>> _waitingQueue = new();
-
-      public static async Task<InternalConnection> GetConnection(string connectionString)
-      {
-         InternalConnection internalConnection = null;
-
-         if (_connectionsCreated)
-         {
-            if (!_stack.TryPop(out internalConnection))
-            {
-               internalConnection = await GetDbConnectionAsync();
-            }
-
-            return internalConnection;
-         }
-         else
-         {
-            internalConnection = new InternalConnection();
-            internalConnection.DbConnection = new System.Data.Odbc.OdbcConnection(connectionString);
-
-            _createdConnections++;
-
-            if (_createdConnections == _maxConnections) _connectionsCreated = true;
-
-            internalConnection.Number = _createdConnections;
-            internalConnection.DbCommands = new ConcurrentDictionary<string, DbCommand>();
-            //Console.WriteLine("opened connection number: " + dbConnection.Number);
-
-            return internalConnection;
-         }
-      }
-
-      public static Task<InternalConnection> GetDbConnectionAsync()
-      {
-         var taskCompletionSource = new TaskCompletionSource<InternalConnection>(TaskCreationOptions.RunContinuationsAsynchronously);
-
-         _waitingQueue.Enqueue(taskCompletionSource);
-         return taskCompletionSource.Task;
-      }
-
-      public static void Release(InternalConnection internalConnection)
-      {
-         TaskCompletionSource<InternalConnection> taskCompletionSource;
-
-         if (_waitingQueue.TryDequeue(out taskCompletionSource))
-         {
-            taskCompletionSource.SetResult(internalConnection);
-         }
-         else
-         {
-            _stack.Push(internalConnection);
-         }
-      }
-   }
-}

+ 0 - 17
frameworks/CSharp/appmpower/src/Data/DbProviderFactory.cs

@@ -1,17 +0,0 @@
-using System.Data;
-
-namespace appMpower.Data
-{
-   public static class DbProviderFactory
-   {
-#if MYSQL
-      public const string ConnectionString = "Driver={MariaDB};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;Pooling=false;OPTIONS=67108864;FLAG_FORWARD_CURSOR=1"; 
-#elif ADO
-      public const string ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=0;Maximum Pool Size=18;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000"; 
-      //public const string ConnectionString = "Server=localhost;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;SSL Mode=Disable;Maximum Pool Size=18;NoResetOnClose=true;Enlist=false;Max Auto Prepare=4;Multiplexing=true;Write Coalescing Buffer Threshold Bytes=1000"; 
-#else
-      public const string ConnectionString = "Driver={PostgreSQL};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false";
-      //public const string ConnectionString = "Driver={PostgreSQL};Server=localhost;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false";
-#endif
-   }
-}

+ 0 - 16
frameworks/CSharp/appmpower/src/Data/InternalConnection.cs

@@ -1,16 +0,0 @@
-using System.Collections.Concurrent;
-using System.Data;
-
-namespace appMpower.Data
-{
-   public class InternalConnection : System.IDisposable
-   {
-      public short Number { get; set; }
-      public IDbConnection DbConnection { get; set; }
-      public ConcurrentDictionary<string, DbCommand> DbCommands { get; set; }
-
-      public void Dispose()
-      {
-      }
-   }
-}

+ 0 - 117
frameworks/CSharp/appmpower/src/HttpApplication.cs

@@ -1,117 +0,0 @@
-using System;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Hosting.Server;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Http.Features;
-using appMpower.Kestrel;
-
-namespace appMpower
-{
-   public class HttpApplication : IHttpApplication<IFeatureCollection>
-   {
-      public static readonly byte[] _plainText = Encoding.UTF8.GetBytes("Hello, World!");
-      private readonly static JsonMessageSerializer _jsonMessageSerializer = new JsonMessageSerializer();
-      private readonly static WorldSerializer _worldSerializer = new WorldSerializer();
-      private readonly static CachedWorldSerializer _cachedWorldSerializer = new CachedWorldSerializer();
-
-      public IFeatureCollection CreateContext(IFeatureCollection featureCollection)
-      {
-         return featureCollection;
-      }
-
-      public async Task ProcessRequestAsync(IFeatureCollection featureCollection)
-      {
-         var request = featureCollection as IHttpRequestFeature;
-         var httpResponse = featureCollection as IHttpResponseFeature;
-         var httpResponseBody = featureCollection as IHttpResponseBodyFeature;
-
-         PathString pathString = request.Path;
-
-         if (pathString.HasValue)
-         {
-            int pathStringLength = pathString.Value.Length;
-            string pathStringStart = pathString.Value.Substring(1, 1);
-
-            if (pathStringLength == 10 && pathStringStart == "p")
-            {
-               //await PlainText.RenderAsync(httpResponse.Headers, httpResponseBody.Writer, _plainText);
-               PlainText.Render(httpResponse.Headers, httpResponseBody, _plainText);
-               return;
-            }
-            else if (pathStringLength == 5 && pathStringStart == "j")
-            {
-               Json.RenderOne(httpResponse.Headers, httpResponseBody.Writer, new JsonMessage { message = "Hello, World!" }, _jsonMessageSerializer);
-               return;
-            }
-            else if (pathStringLength == 3 && pathStringStart == "d")
-            {
-               Json.RenderOne(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadSingleQueryRow(), _worldSerializer);
-               return;
-            }
-            else if (pathStringLength == 8 && pathStringStart == "q")
-            {
-               int count = 1;
-
-               if (!Int32.TryParse(request.QueryString.Substring(request.QueryString.LastIndexOf("=") + 1), out count) || count < 1)
-               {
-                  count = 1;
-               }
-               else if (count > 500)
-               {
-                  count = 500;
-               }
-
-#if ADO
-               Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadMultipleQueriesRows(count), _worldSerializer);
-#else
-               Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.ReadMultipleRows(count), _worldSerializer);
-#endif
-
-               return;
-            }
-            else if (pathStringLength == 9 && pathStringStart == "f")
-            {
-               await FortunesView.Render(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadFortunesRows());
-               return;
-            }
-            else if (pathStringLength == 8 && pathStringStart == "u")
-            {
-               int count = 1;
-
-               if (!Int32.TryParse(request.QueryString.Substring(request.QueryString.LastIndexOf("=") + 1), out count) || count < 1)
-               {
-                  count = 1;
-               }
-               else if (count > 500)
-               {
-                  count = 500;
-               }
-
-               Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadMultipleUpdatesRows(count), _worldSerializer);
-               return;
-            }
-            else if (pathStringLength == 14 && pathStringStart == "c")
-            {
-               int count = 1;
-
-               if (!Int32.TryParse(request.QueryString.Substring(request.QueryString.LastIndexOf("=") + 1), out count) || count < 1)
-               {
-                  count = 1;
-               }
-               else if (count > 500)
-               {
-                  count = 500;
-               }
-
-               Json.RenderMany(httpResponse.Headers, httpResponseBody.Writer, await RawDb.LoadCachedQueries(count), _cachedWorldSerializer);
-               return;
-            }
-         }
-      }
-
-      public void DisposeContext(IFeatureCollection featureCollection, Exception exception)
-      {
-      }
-   }
-}

+ 0 - 7
frameworks/CSharp/appmpower/src/JsonMessage.cs

@@ -1,7 +0,0 @@
-namespace appMpower
-{
-   public struct JsonMessage
-   {
-      public string message { get; set; }
-   }
-}

+ 0 - 58
frameworks/CSharp/appmpower/src/Kestrel/Json.cs

@@ -1,58 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO.Pipelines;
-using System.Text.Json;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Primitives;
-
-namespace appMpower.Kestrel
-{
-   public static class Json
-   {
-      private readonly static KeyValuePair<string, StringValues> _headerServer =
-         new KeyValuePair<string, StringValues>("Server", "k");
-      private readonly static KeyValuePair<string, StringValues> _headerContentType =
-         new KeyValuePair<string, StringValues>("Content-Type", "application/json");
-
-      [ThreadStatic]
-      private static Utf8JsonWriter _utf8JsonWriter;
-
-      public static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions
-      {
-         SkipValidation = true
-      };
-
-      public static void RenderOne<T>(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, T t, IJsonSerializer<T> jsonSerializer)
-      {
-         headerDictionary.Add(_headerServer);
-         headerDictionary.Add(_headerContentType);
-
-         Utf8JsonWriter utf8JsonWriter = _utf8JsonWriter ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true });
-         utf8JsonWriter.Reset(pipeWriter);
-
-         jsonSerializer.Serialize(utf8JsonWriter, t);
-         utf8JsonWriter.Flush();
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", utf8JsonWriter.BytesCommitted.ToString()));
-      }
-
-      public static void RenderMany<T>(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, T[] tArray, IJsonSerializer<T> jsonSerializer)
-      {
-         headerDictionary.Add(_headerServer);
-         headerDictionary.Add(_headerContentType);
-
-         Utf8JsonWriter utf8JsonWriter = _utf8JsonWriter ??= new Utf8JsonWriter(pipeWriter, new JsonWriterOptions { SkipValidation = true });
-         utf8JsonWriter.Reset(pipeWriter);
-
-         utf8JsonWriter.WriteStartArray();
-
-         foreach (var t in tArray)
-         {
-            jsonSerializer.Serialize(utf8JsonWriter, t);
-         }
-
-         utf8JsonWriter.WriteEndArray();
-         utf8JsonWriter.Flush();
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", utf8JsonWriter.BytesCommitted.ToString()));
-      }
-   }
-}

+ 0 - 38
frameworks/CSharp/appmpower/src/Kestrel/PlainText.cs

@@ -1,38 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using System.Collections.Generic;
-using System.IO.Pipelines;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Primitives;
-using Microsoft.AspNetCore.Http.Features;
-
-namespace appMpower.Kestrel
-{
-   public static class PlainText
-   {
-      private readonly static KeyValuePair<string, StringValues> _headerServer =
-         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
-      private readonly static KeyValuePair<string, StringValues> _headerContentType =
-         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("text/plain"));
-
-      public static async Task RenderAsync(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, ReadOnlyMemory<byte> utf8String)
-      {
-         headerDictionary.Add(_headerServer);
-         headerDictionary.Add(_headerContentType);
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", utf8String.Length.ToString()));
-
-         await pipeWriter.WriteAsync(utf8String);
-         pipeWriter.Complete();
-      }
-
-      public static void Render(IHeaderDictionary headerDictionary, IHttpResponseBodyFeature httpResponseBodyFeature, byte[] utf8String)
-      {
-         headerDictionary.Add(_headerServer);
-         headerDictionary.Add(_headerContentType);
-         int length = utf8String.Length;
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", length.ToString()));
-
-         httpResponseBodyFeature.Stream.Write(utf8String, 0, length);
-      }
-   }
-}

+ 0 - 25
frameworks/CSharp/appmpower/src/Kestrel/ServiceProvider.cs

@@ -1,25 +0,0 @@
-using System;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Abstractions;
-
-namespace appMpower.Kestrel
-{
-   public class ServiceProvider : ISupportRequiredService, IServiceProvider
-   {
-      public object GetRequiredService(Type serviceType)
-      {
-         return GetService(serviceType);
-      }
-
-      public object GetService(Type serviceType)
-      {
-         if (serviceType == typeof(ILoggerFactory))
-         {
-            return NullLoggerFactory.Instance;
-         }
-
-         return null;
-      }
-   }
-}

+ 0 - 257
frameworks/CSharp/appmpower/src/Memory/CacheEntry.cs

@@ -1,257 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Primitives;
-using Microsoft.Extensions.Caching.Memory;
-
-namespace appMpower.Memory
-{
-   internal sealed partial class CacheEntry : ICacheEntry
-   {
-      private static readonly Action<object> ExpirationCallback = ExpirationTokensExpired;
-
-      private readonly MemoryCache _cache;
-
-      private CacheEntryTokens _tokens; // might be null if user is not using the tokens or callbacks
-      private TimeSpan? _absoluteExpirationRelativeToNow;
-      private TimeSpan? _slidingExpiration;
-      private long? _size;
-      private CacheEntry _previous; // this field is not null only before the entry is added to the cache and tracking is enabled
-      private object _value;
-      private CacheEntryState _state;
-
-      internal CacheEntry(object key, MemoryCache memoryCache)
-      {
-         Key = key ?? throw new ArgumentNullException(nameof(key));
-         _cache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
-         _previous = memoryCache.TrackLinkedCacheEntries ? CacheEntryHelper.EnterScope(this) : null;
-         _state = new CacheEntryState(CacheItemPriority.Normal);
-      }
-
-      /// <summary>
-      /// Gets or sets an absolute expiration date for the cache entry.
-      /// </summary>
-      public DateTimeOffset? AbsoluteExpiration { get; set; }
-
-      /// <summary>
-      /// Gets or sets an absolute expiration time, relative to now.
-      /// </summary>
-      public TimeSpan? AbsoluteExpirationRelativeToNow
-      {
-         get => _absoluteExpirationRelativeToNow;
-         set
-         {
-            // this method does not set AbsoluteExpiration as it would require calling Clock.UtcNow twice:
-            // once here and once in MemoryCache.SetEntry
-
-            if (value <= TimeSpan.Zero)
-            {
-               throw new ArgumentOutOfRangeException(
-                   nameof(AbsoluteExpirationRelativeToNow),
-                   value,
-                   "The relative expiration value must be positive.");
-            }
-
-            _absoluteExpirationRelativeToNow = value;
-         }
-      }
-
-      /// <summary>
-      /// Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed.
-      /// This will not extend the entry lifetime beyond the absolute expiration (if set).
-      /// </summary>
-      public TimeSpan? SlidingExpiration
-      {
-         get => _slidingExpiration;
-         set
-         {
-            if (value <= TimeSpan.Zero)
-            {
-               throw new ArgumentOutOfRangeException(
-                   nameof(SlidingExpiration),
-                   value,
-                   "The sliding expiration value must be positive.");
-            }
-
-            _slidingExpiration = value;
-         }
-      }
-
-      /// <summary>
-      /// Gets the <see cref="IChangeToken"/> instances which cause the cache entry to expire.
-      /// </summary>
-      public IList<IChangeToken> ExpirationTokens => GetOrCreateTokens().ExpirationTokens;
-
-      /// <summary>
-      /// Gets or sets the callbacks will be fired after the cache entry is evicted from the cache.
-      /// </summary>
-      public IList<PostEvictionCallbackRegistration> PostEvictionCallbacks => GetOrCreateTokens().PostEvictionCallbacks;
-
-      /// <summary>
-      /// Gets or sets the priority for keeping the cache entry in the cache during a
-      /// memory pressure triggered cleanup. The default is <see cref="CacheItemPriority.Normal"/>.
-      /// </summary>
-      public CacheItemPriority Priority { get => _state.Priority; set => _state.Priority = value; }
-
-      /// <summary>
-      /// Gets or sets the size of the cache entry value.
-      /// </summary>
-      public long? Size
-      {
-         get => _size;
-         set
-         {
-            if (value < 0)
-            {
-               throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be non-negative.");
-            }
-
-            _size = value;
-         }
-      }
-
-      public object Key { get; private set; }
-
-      public object Value
-      {
-         get => _value;
-         set
-         {
-            _value = value;
-            _state.IsValueSet = true;
-         }
-      }
-
-      internal DateTimeOffset LastAccessed { get; set; }
-
-      internal EvictionReason EvictionReason { get => _state.EvictionReason; private set => _state.EvictionReason = value; }
-
-      public void Dispose()
-      {
-         if (!_state.IsDisposed)
-         {
-            _state.IsDisposed = true;
-
-            if (_cache.TrackLinkedCacheEntries)
-            {
-               CacheEntryHelper.ExitScope(this, _previous);
-            }
-
-            // Don't commit or propagate options if the CacheEntry Value was never set.
-            // We assume an exception occurred causing the caller to not set the Value successfully,
-            // so don't use this entry.
-            if (_state.IsValueSet)
-            {
-               _cache.SetEntry(this);
-
-               if (_previous != null && CanPropagateOptions())
-               {
-                  PropagateOptions(_previous);
-               }
-            }
-
-            _previous = null; // we don't want to root unnecessary objects
-         }
-      }
-
-      [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling
-      internal bool CheckExpired(in DateTimeOffset now)
-          => _state.IsExpired
-              || CheckForExpiredTime(now)
-              || (_tokens != null && _tokens.CheckForExpiredTokens(this));
-
-      internal void SetExpired(EvictionReason reason)
-      {
-         if (EvictionReason == EvictionReason.None)
-         {
-            EvictionReason = reason;
-         }
-         _state.IsExpired = true;
-         _tokens?.DetachTokens();
-      }
-
-      [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling
-      private bool CheckForExpiredTime(in DateTimeOffset now)
-      {
-         if (!AbsoluteExpiration.HasValue && !_slidingExpiration.HasValue)
-         {
-            return false;
-         }
-
-         return FullCheck(now);
-
-         bool FullCheck(in DateTimeOffset offset)
-         {
-            if (AbsoluteExpiration.HasValue && AbsoluteExpiration.Value <= offset)
-            {
-               SetExpired(EvictionReason.Expired);
-               return true;
-            }
-
-            if (_slidingExpiration.HasValue
-                && (offset - LastAccessed) >= _slidingExpiration)
-            {
-               SetExpired(EvictionReason.Expired);
-               return true;
-            }
-
-            return false;
-         }
-      }
-
-      internal void AttachTokens() => _tokens?.AttachTokens(this);
-
-      private static void ExpirationTokensExpired(object obj)
-      {
-         // start a new thread to avoid issues with callbacks called from RegisterChangeCallback
-         Task.Factory.StartNew(state =>
-         {
-            var entry = (CacheEntry)state;
-            entry.SetExpired(EvictionReason.TokenExpired);
-            entry._cache.EntryExpired(entry);
-         }, obj, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-      }
-
-      internal void InvokeEvictionCallbacks() => _tokens?.InvokeEvictionCallbacks(this);
-
-      // this simple check very often allows us to avoid expensive call to PropagateOptions(CacheEntryHelper.Current)
-      [MethodImpl(MethodImplOptions.AggressiveInlining)] // added based on profiling
-      internal bool CanPropagateOptions() => (_tokens != null && _tokens.CanPropagateTokens()) || AbsoluteExpiration.HasValue;
-
-      internal void PropagateOptions(CacheEntry parent)
-      {
-         if (parent == null)
-         {
-            return;
-         }
-
-         // Copy expiration tokens and AbsoluteExpiration to the cache entries hierarchy.
-         // We do this regardless of it gets cached because the tokens are associated with the value we'll return.
-         _tokens?.PropagateTokens(parent);
-
-         if (AbsoluteExpiration.HasValue)
-         {
-            if (!parent.AbsoluteExpiration.HasValue || AbsoluteExpiration < parent.AbsoluteExpiration)
-            {
-               parent.AbsoluteExpiration = AbsoluteExpiration;
-            }
-         }
-      }
-
-      private CacheEntryTokens GetOrCreateTokens()
-      {
-         if (_tokens != null)
-         {
-            return _tokens;
-         }
-
-         CacheEntryTokens result = new CacheEntryTokens();
-         return Interlocked.CompareExchange(ref _tokens, result, null) ?? result;
-      }
-   }
-}

+ 0 - 32
frameworks/CSharp/appmpower/src/Memory/CacheEntryHelper.cs

@@ -1,32 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Diagnostics;
-using System.Threading;
-
-namespace appMpower.Memory
-{
-   internal static class CacheEntryHelper
-   {
-      private static readonly AsyncLocal<CacheEntry> _current = new AsyncLocal<CacheEntry>();
-
-      internal static CacheEntry Current
-      {
-         get => _current.Value;
-         private set => _current.Value = value;
-      }
-
-      internal static CacheEntry EnterScope(CacheEntry current)
-      {
-         CacheEntry previous = Current;
-         Current = current;
-         return previous;
-      }
-
-      internal static void ExitScope(CacheEntry current, CacheEntry previous)
-      {
-         Debug.Assert(Current == current, "Entries disposed in invalid order");
-         Current = previous;
-      }
-   }
-}

+ 0 - 62
frameworks/CSharp/appmpower/src/Memory/CacheEntryState.cs

@@ -1,62 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using Microsoft.Extensions.Caching.Memory;
-
-namespace appMpower.Memory
-{
-   internal sealed partial class CacheEntry
-   {
-      // this type exists just to reduce CacheEntry size by replacing many enum & boolean fields with one of a size of Int32
-      private struct CacheEntryState
-      {
-         private byte _flags;
-         private byte _evictionReason;
-         private byte _priority;
-
-         internal CacheEntryState(CacheItemPriority priority) : this() => _priority = (byte)priority;
-
-         internal bool IsDisposed
-         {
-            get => ((Flags)_flags & Flags.IsDisposed) != 0;
-            set => SetFlag(Flags.IsDisposed, value);
-         }
-
-         internal bool IsExpired
-         {
-            get => ((Flags)_flags & Flags.IsExpired) != 0;
-            set => SetFlag(Flags.IsExpired, value);
-         }
-
-         internal bool IsValueSet
-         {
-            get => ((Flags)_flags & Flags.IsValueSet) != 0;
-            set => SetFlag(Flags.IsValueSet, value);
-         }
-
-         internal EvictionReason EvictionReason
-         {
-            get => (EvictionReason)_evictionReason;
-            set => _evictionReason = (byte)value;
-         }
-
-         internal CacheItemPriority Priority
-         {
-            get => (CacheItemPriority)_priority;
-            set => _priority = (byte)value;
-         }
-
-         private void SetFlag(Flags option, bool value) => _flags = (byte)(value ? (_flags | (byte)option) : (_flags & ~(byte)option));
-
-         [Flags]
-         private enum Flags : byte
-         {
-            Default = 0,
-            IsValueSet = 1 << 0,
-            IsExpired = 1 << 1,
-            IsDisposed = 1 << 2,
-         }
-      }
-   }
-}

+ 0 - 140
frameworks/CSharp/appmpower/src/Memory/CacheEntryTokens.cs

@@ -1,140 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Primitives;
-using Microsoft.Extensions.Caching.Memory;
-
-namespace appMpower.Memory
-{
-   internal sealed partial class CacheEntry
-   {
-      // this type exists just to reduce average CacheEntry size
-      // which typically is not using expiration tokens or callbacks
-      private sealed class CacheEntryTokens
-      {
-         private List<IChangeToken> _expirationTokens;
-         private List<IDisposable> _expirationTokenRegistrations;
-         private List<PostEvictionCallbackRegistration> _postEvictionCallbacks; // this is not really related to tokens, but was moved here to shrink typical CacheEntry size
-
-         internal List<IChangeToken> ExpirationTokens => _expirationTokens ??= new List<IChangeToken>();
-         internal List<PostEvictionCallbackRegistration> PostEvictionCallbacks => _postEvictionCallbacks ??= new List<PostEvictionCallbackRegistration>();
-
-         internal void AttachTokens(CacheEntry cacheEntry)
-         {
-            if (_expirationTokens != null)
-            {
-               lock (this)
-               {
-                  for (int i = 0; i < _expirationTokens.Count; i++)
-                  {
-                     IChangeToken expirationToken = _expirationTokens[i];
-                     if (expirationToken.ActiveChangeCallbacks)
-                     {
-                        _expirationTokenRegistrations ??= new List<IDisposable>(1);
-                        IDisposable registration = expirationToken.RegisterChangeCallback(ExpirationCallback, cacheEntry);
-                        _expirationTokenRegistrations.Add(registration);
-                     }
-                  }
-               }
-            }
-         }
-
-         internal bool CheckForExpiredTokens(CacheEntry cacheEntry)
-         {
-            if (_expirationTokens != null)
-            {
-               for (int i = 0; i < _expirationTokens.Count; i++)
-               {
-                  IChangeToken expiredToken = _expirationTokens[i];
-                  if (expiredToken.HasChanged)
-                  {
-                     cacheEntry.SetExpired(EvictionReason.TokenExpired);
-                     return true;
-                  }
-               }
-            }
-            return false;
-         }
-
-         internal bool CanPropagateTokens() => _expirationTokens != null;
-
-         internal void PropagateTokens(CacheEntry parentEntry)
-         {
-            if (_expirationTokens != null)
-            {
-               lock (this)
-               {
-                  lock (parentEntry.GetOrCreateTokens())
-                  {
-                     foreach (IChangeToken expirationToken in _expirationTokens)
-                     {
-                        parentEntry.AddExpirationToken(expirationToken);
-                     }
-                  }
-               }
-            }
-         }
-
-         internal void DetachTokens()
-         {
-            // _expirationTokenRegistrations is not checked for null, because AttachTokens might initialize it under lock
-            // instead we are checking for _expirationTokens, because if they are not null, then _expirationTokenRegistrations might also be not null
-            if (_expirationTokens != null)
-            {
-               lock (this)
-               {
-                  List<IDisposable> registrations = _expirationTokenRegistrations;
-                  if (registrations != null)
-                  {
-                     _expirationTokenRegistrations = null;
-                     for (int i = 0; i < registrations.Count; i++)
-                     {
-                        IDisposable registration = registrations[i];
-                        registration.Dispose();
-                     }
-                  }
-               }
-            }
-         }
-
-         internal void InvokeEvictionCallbacks(CacheEntry cacheEntry)
-         {
-            if (_postEvictionCallbacks != null)
-            {
-               Task.Factory.StartNew(state => InvokeCallbacks((CacheEntry)state), cacheEntry,
-                   CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-            }
-         }
-
-         private static void InvokeCallbacks(CacheEntry entry)
-         {
-            List<PostEvictionCallbackRegistration> callbackRegistrations = Interlocked.Exchange(ref entry._tokens._postEvictionCallbacks, null);
-
-            if (callbackRegistrations == null)
-            {
-               return;
-            }
-
-            for (int i = 0; i < callbackRegistrations.Count; i++)
-            {
-               PostEvictionCallbackRegistration registration = callbackRegistrations[i];
-
-               try
-               {
-                  registration.EvictionCallback?.Invoke(entry.Key, entry.Value, entry.EvictionReason, registration.State);
-               }
-               catch (Exception e)
-               {
-                  // This will be invoked on a background thread, don't let it throw.
-                  entry._cache._logger.LogError(e, "EvictionCallback invoked failed");
-               }
-            }
-         }
-      }
-   }
-}

+ 0 - 520
frameworks/CSharp/appmpower/src/Memory/MemoryCache.cs

@@ -1,520 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Internal;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Options;
-using Microsoft.Extensions.Caching.Memory;
-
-namespace appMpower.Memory
-{
-   /// <summary>
-   /// An implementation of <see cref="IMemoryCache"/> using a dictionary to
-   /// store its entries.
-   /// </summary>
-   public class MemoryCache : IMemoryCache
-   {
-      internal readonly ILogger _logger;
-
-      private readonly MemoryCacheOptions _options;
-      private readonly ConcurrentDictionary<object, CacheEntry> _entries;
-
-      private long _cacheSize;
-      private bool _disposed;
-      private DateTimeOffset _lastExpirationScan;
-
-      /// <summary>
-      /// Creates a new <see cref="MemoryCache"/> instance.
-      /// </summary>
-      /// <param name="optionsAccessor">The options of the cache.</param>
-      public MemoryCache(IOptions<MemoryCacheOptions> optionsAccessor)
-          : this(optionsAccessor, NullLoggerFactory.Instance) { }
-
-      /// <summary>
-      /// Creates a new <see cref="MemoryCache"/> instance.
-      /// </summary>
-      /// <param name="optionsAccessor">The options of the cache.</param>
-      /// <param name="loggerFactory">The factory used to create loggers.</param>
-      public MemoryCache(IOptions<MemoryCacheOptions> optionsAccessor, ILoggerFactory loggerFactory)
-      {
-         if (optionsAccessor == null)
-         {
-            throw new ArgumentNullException(nameof(optionsAccessor));
-         }
-
-         if (loggerFactory == null)
-         {
-            throw new ArgumentNullException(nameof(loggerFactory));
-         }
-
-         _options = optionsAccessor.Value;
-         //_logger = loggerFactory.CreateLogger<MemoryCache>();
-         _logger = loggerFactory.CreateLogger("MemoryCache");
-
-         _entries = new ConcurrentDictionary<object, CacheEntry>();
-
-         if (_options.Clock == null)
-         {
-            _options.Clock = new SystemClock();
-         }
-
-         _lastExpirationScan = _options.Clock.UtcNow;
-         TrackLinkedCacheEntries = _options.TrackLinkedCacheEntries; // we store the setting now so it's consistent for entire MemoryCache lifetime
-      }
-
-      /// <summary>
-      /// Cleans up the background collection events.
-      /// </summary>
-      ~MemoryCache() => Dispose(false);
-
-      /// <summary>
-      /// Gets the count of the current entries for diagnostic purposes.
-      /// </summary>
-      public int Count => _entries.Count;
-
-      // internal for testing
-      internal long Size { get => Interlocked.Read(ref _cacheSize); }
-
-      internal bool TrackLinkedCacheEntries { get; }
-
-      private ICollection<KeyValuePair<object, CacheEntry>> EntriesCollection => _entries;
-
-      /// <inheritdoc />
-      public ICacheEntry CreateEntry(object key)
-      {
-         CheckDisposed();
-         ValidateCacheKey(key);
-
-         return new CacheEntry(key, this);
-      }
-
-      internal void SetEntry(CacheEntry entry)
-      {
-         if (_disposed)
-         {
-            // No-op instead of throwing since this is called during CacheEntry.Dispose
-            return;
-         }
-
-         if (_options.SizeLimit.HasValue && !entry.Size.HasValue)
-         {
-            //throw new InvalidOperationException(SR.Format(SR.CacheEntryHasEmptySize, nameof(entry.Size), nameof(_options.SizeLimit)));
-            throw new InvalidOperationException();
-         }
-
-         DateTimeOffset utcNow = _options.Clock.UtcNow;
-
-         DateTimeOffset? absoluteExpiration = null;
-         if (entry.AbsoluteExpirationRelativeToNow.HasValue)
-         {
-            absoluteExpiration = utcNow + entry.AbsoluteExpirationRelativeToNow;
-         }
-         else if (entry.AbsoluteExpiration.HasValue)
-         {
-            absoluteExpiration = entry.AbsoluteExpiration;
-         }
-
-         // Applying the option's absolute expiration only if it's not already smaller.
-         // This can be the case if a dependent cache entry has a smaller value, and
-         // it was set by cascading it to its parent.
-         if (absoluteExpiration.HasValue)
-         {
-            if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration.Value < entry.AbsoluteExpiration.Value)
-            {
-               entry.AbsoluteExpiration = absoluteExpiration;
-            }
-         }
-
-         // Initialize the last access timestamp at the time the entry is added
-         entry.LastAccessed = utcNow;
-
-         if (_entries.TryGetValue(entry.Key, out CacheEntry priorEntry))
-         {
-            priorEntry.SetExpired(EvictionReason.Replaced);
-         }
-
-         bool exceedsCapacity = UpdateCacheSizeExceedsCapacity(entry);
-
-         if (!entry.CheckExpired(utcNow) && !exceedsCapacity)
-         {
-            bool entryAdded = false;
-
-            if (priorEntry == null)
-            {
-               // Try to add the new entry if no previous entries exist.
-               entryAdded = _entries.TryAdd(entry.Key, entry);
-            }
-            else
-            {
-               // Try to update with the new entry if a previous entries exist.
-               entryAdded = _entries.TryUpdate(entry.Key, entry, priorEntry);
-
-               if (entryAdded)
-               {
-                  if (_options.SizeLimit.HasValue)
-                  {
-                     // The prior entry was removed, decrease the by the prior entry's size
-                     Interlocked.Add(ref _cacheSize, -priorEntry.Size.Value);
-                  }
-               }
-               else
-               {
-                  // The update will fail if the previous entry was removed after retrival.
-                  // Adding the new entry will succeed only if no entry has been added since.
-                  // This guarantees removing an old entry does not prevent adding a new entry.
-                  entryAdded = _entries.TryAdd(entry.Key, entry);
-               }
-            }
-
-            if (entryAdded)
-            {
-               entry.AttachTokens();
-            }
-            else
-            {
-               if (_options.SizeLimit.HasValue)
-               {
-                  // Entry could not be added, reset cache size
-                  Interlocked.Add(ref _cacheSize, -entry.Size.Value);
-               }
-               entry.SetExpired(EvictionReason.Replaced);
-               entry.InvokeEvictionCallbacks();
-            }
-
-            if (priorEntry != null)
-            {
-               priorEntry.InvokeEvictionCallbacks();
-            }
-         }
-         else
-         {
-            if (exceedsCapacity)
-            {
-               // The entry was not added due to overcapacity
-               entry.SetExpired(EvictionReason.Capacity);
-
-               TriggerOvercapacityCompaction();
-            }
-            else
-            {
-               if (_options.SizeLimit.HasValue)
-               {
-                  // Entry could not be added due to being expired, reset cache size
-                  Interlocked.Add(ref _cacheSize, -entry.Size.Value);
-               }
-            }
-
-            entry.InvokeEvictionCallbacks();
-            if (priorEntry != null)
-            {
-               RemoveEntry(priorEntry);
-            }
-         }
-
-         StartScanForExpiredItemsIfNeeded(utcNow);
-      }
-
-      /// <inheritdoc />
-      public bool TryGetValue(object key, out object result)
-      {
-         ValidateCacheKey(key);
-         CheckDisposed();
-
-         DateTimeOffset utcNow = _options.Clock.UtcNow;
-
-         if (_entries.TryGetValue(key, out CacheEntry entry))
-         {
-            // Check if expired due to expiration tokens, timers, etc. and if so, remove it.
-            // Allow a stale Replaced value to be returned due to concurrent calls to SetExpired during SetEntry.
-            if (!entry.CheckExpired(utcNow) || entry.EvictionReason == EvictionReason.Replaced)
-            {
-               entry.LastAccessed = utcNow;
-               result = entry.Value;
-
-               if (TrackLinkedCacheEntries && entry.CanPropagateOptions())
-               {
-                  // When this entry is retrieved in the scope of creating another entry,
-                  // that entry needs a copy of these expiration tokens.
-                  entry.PropagateOptions(CacheEntryHelper.Current);
-               }
-
-               StartScanForExpiredItemsIfNeeded(utcNow);
-
-               return true;
-            }
-            else
-            {
-               // TODO: For efficiency queue this up for batch removal
-               RemoveEntry(entry);
-            }
-         }
-
-         StartScanForExpiredItemsIfNeeded(utcNow);
-
-         result = null;
-         return false;
-      }
-
-      /// <inheritdoc />
-      public void Remove(object key)
-      {
-         ValidateCacheKey(key);
-
-         CheckDisposed();
-         if (_entries.TryRemove(key, out CacheEntry entry))
-         {
-            if (_options.SizeLimit.HasValue)
-            {
-               Interlocked.Add(ref _cacheSize, -entry.Size.Value);
-            }
-
-            entry.SetExpired(EvictionReason.Removed);
-            entry.InvokeEvictionCallbacks();
-         }
-
-         StartScanForExpiredItemsIfNeeded(_options.Clock.UtcNow);
-      }
-
-      private void RemoveEntry(CacheEntry entry)
-      {
-         if (EntriesCollection.Remove(new KeyValuePair<object, CacheEntry>(entry.Key, entry)))
-         {
-            if (_options.SizeLimit.HasValue)
-            {
-               Interlocked.Add(ref _cacheSize, -entry.Size.Value);
-            }
-            entry.InvokeEvictionCallbacks();
-         }
-      }
-
-      internal void EntryExpired(CacheEntry entry)
-      {
-         // TODO: For efficiency consider processing these expirations in batches.
-         RemoveEntry(entry);
-         StartScanForExpiredItemsIfNeeded(_options.Clock.UtcNow);
-      }
-
-      // Called by multiple actions to see how long it's been since we last checked for expired items.
-      // If sufficient time has elapsed then a scan is initiated on a background task.
-      [MethodImpl(MethodImplOptions.AggressiveInlining)]
-      private void StartScanForExpiredItemsIfNeeded(DateTimeOffset utcNow)
-      {
-         if (_options.ExpirationScanFrequency < utcNow - _lastExpirationScan)
-         {
-            ScheduleTask(utcNow);
-         }
-
-         void ScheduleTask(DateTimeOffset utcNow)
-         {
-            _lastExpirationScan = utcNow;
-            Task.Factory.StartNew(state => ScanForExpiredItems((MemoryCache)state), this,
-                CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
-         }
-      }
-
-      private static void ScanForExpiredItems(MemoryCache cache)
-      {
-         DateTimeOffset now = cache._lastExpirationScan = cache._options.Clock.UtcNow;
-
-         foreach (KeyValuePair<object, CacheEntry> item in cache._entries)
-         {
-            CacheEntry entry = item.Value;
-
-            if (entry.CheckExpired(now))
-            {
-               cache.RemoveEntry(entry);
-            }
-         }
-      }
-
-      private bool UpdateCacheSizeExceedsCapacity(CacheEntry entry)
-      {
-         if (!_options.SizeLimit.HasValue)
-         {
-            return false;
-         }
-
-         long newSize = 0L;
-         for (int i = 0; i < 100; i++)
-         {
-            long sizeRead = Interlocked.Read(ref _cacheSize);
-            newSize = sizeRead + entry.Size.Value;
-
-            if (newSize < 0 || newSize > _options.SizeLimit)
-            {
-               // Overflow occurred, return true without updating the cache size
-               return true;
-            }
-
-            if (sizeRead == Interlocked.CompareExchange(ref _cacheSize, newSize, sizeRead))
-            {
-               return false;
-            }
-         }
-
-         return true;
-      }
-
-      private void TriggerOvercapacityCompaction()
-      {
-         _logger.LogDebug("Overcapacity compaction triggered");
-
-         // Spawn background thread for compaction
-         ThreadPool.QueueUserWorkItem(s => OvercapacityCompaction((MemoryCache)s), this);
-      }
-
-      private static void OvercapacityCompaction(MemoryCache cache)
-      {
-         long currentSize = Interlocked.Read(ref cache._cacheSize);
-
-         cache._logger.LogDebug($"Overcapacity compaction executing. Current size {currentSize}");
-
-         double? lowWatermark = cache._options.SizeLimit * (1 - cache._options.CompactionPercentage);
-         if (currentSize > lowWatermark)
-         {
-            cache.Compact(currentSize - (long)lowWatermark, entry => entry.Size.Value);
-         }
-
-         cache._logger.LogDebug($"Overcapacity compaction executed. New size {Interlocked.Read(ref cache._cacheSize)}");
-      }
-
-      /// Remove at least the given percentage (0.10 for 10%) of the total entries (or estimated memory?), according to the following policy:
-      /// 1. Remove all expired items.
-      /// 2. Bucket by CacheItemPriority.
-      /// 3. Least recently used objects.
-      /// ?. Items with the soonest absolute expiration.
-      /// ?. Items with the soonest sliding expiration.
-      /// ?. Larger objects - estimated by object graph size, inaccurate.
-      public void Compact(double percentage)
-      {
-         int removalCountTarget = (int)(_entries.Count * percentage);
-         Compact(removalCountTarget, _ => 1);
-      }
-
-      private void Compact(long removalSizeTarget, Func<CacheEntry, long> computeEntrySize)
-      {
-         var entriesToRemove = new List<CacheEntry>();
-         var lowPriEntries = new List<CacheEntry>();
-         var normalPriEntries = new List<CacheEntry>();
-         var highPriEntries = new List<CacheEntry>();
-         long removedSize = 0;
-
-         // Sort items by expired & priority status
-         DateTimeOffset now = _options.Clock.UtcNow;
-         foreach (KeyValuePair<object, CacheEntry> item in _entries)
-         {
-            CacheEntry entry = item.Value;
-            if (entry.CheckExpired(now))
-            {
-               entriesToRemove.Add(entry);
-               removedSize += computeEntrySize(entry);
-            }
-            else
-            {
-               switch (entry.Priority)
-               {
-                  case CacheItemPriority.Low:
-                     lowPriEntries.Add(entry);
-                     break;
-                  case CacheItemPriority.Normal:
-                     normalPriEntries.Add(entry);
-                     break;
-                  case CacheItemPriority.High:
-                     highPriEntries.Add(entry);
-                     break;
-                  case CacheItemPriority.NeverRemove:
-                     break;
-                  default:
-                     throw new NotSupportedException("Not implemented: " + entry.Priority);
-               }
-            }
-         }
-
-         ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, lowPriEntries);
-         ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, normalPriEntries);
-         ExpirePriorityBucket(ref removedSize, removalSizeTarget, computeEntrySize, entriesToRemove, highPriEntries);
-
-         foreach (CacheEntry entry in entriesToRemove)
-         {
-            RemoveEntry(entry);
-         }
-
-         // Policy:
-         // 1. Least recently used objects.
-         // ?. Items with the soonest absolute expiration.
-         // ?. Items with the soonest sliding expiration.
-         // ?. Larger objects - estimated by object graph size, inaccurate.
-         static void ExpirePriorityBucket(ref long removedSize, long removalSizeTarget, Func<CacheEntry, long> computeEntrySize, List<CacheEntry> entriesToRemove, List<CacheEntry> priorityEntries)
-         {
-            // Do we meet our quota by just removing expired entries?
-            if (removalSizeTarget <= removedSize)
-            {
-               // No-op, we've met quota
-               return;
-            }
-
-            // Expire enough entries to reach our goal
-            // TODO: Refine policy
-
-            // LRU
-            priorityEntries.Sort((e1, e2) => e1.LastAccessed.CompareTo(e2.LastAccessed));
-            foreach (CacheEntry entry in priorityEntries)
-            {
-               entry.SetExpired(EvictionReason.Capacity);
-               entriesToRemove.Add(entry);
-               removedSize += computeEntrySize(entry);
-
-               if (removalSizeTarget <= removedSize)
-               {
-                  break;
-               }
-            }
-         }
-      }
-
-      public void Dispose()
-      {
-         Dispose(true);
-      }
-
-      protected virtual void Dispose(bool disposing)
-      {
-         if (!_disposed)
-         {
-            if (disposing)
-            {
-               GC.SuppressFinalize(this);
-            }
-
-            _disposed = true;
-         }
-      }
-
-      private void CheckDisposed()
-      {
-         if (_disposed)
-         {
-            Throw();
-         }
-
-         static void Throw() => throw new ObjectDisposedException(typeof(MemoryCache).FullName);
-      }
-
-      private static void ValidateCacheKey(object key)
-      {
-         if (key == null)
-         {
-            Throw();
-         }
-
-         static void Throw() => throw new ArgumentNullException(nameof(key));
-      }
-   }
-}

+ 0 - 67
frameworks/CSharp/appmpower/src/Memory/MemoryCacheOptions.cs

@@ -1,67 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using Microsoft.Extensions.Internal;
-using Microsoft.Extensions.Options;
-
-namespace appMpower.Memory
-{
-   public class MemoryCacheOptions : IOptions<MemoryCacheOptions>
-   {
-      private long? _sizeLimit;
-      private double _compactionPercentage = 0.05;
-
-      public ISystemClock Clock { get; set; }
-
-      /// <summary>
-      /// Gets or sets the minimum length of time between successive scans for expired items.
-      /// </summary>
-      public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromMinutes(1);
-
-      /// <summary>
-      /// Gets or sets the maximum size of the cache.
-      /// </summary>
-      public long? SizeLimit
-      {
-         get => _sizeLimit;
-         set
-         {
-            if (value < 0)
-            {
-               throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be non-negative.");
-            }
-
-            _sizeLimit = value;
-         }
-      }
-
-      /// <summary>
-      /// Gets or sets the amount to compact the cache by when the maximum size is exceeded.
-      /// </summary>
-      public double CompactionPercentage
-      {
-         get => _compactionPercentage;
-         set
-         {
-            if (value < 0 || value > 1)
-            {
-               throw new ArgumentOutOfRangeException(nameof(value), value, $"{nameof(value)} must be between 0 and 1 inclusive.");
-            }
-
-            _compactionPercentage = value;
-         }
-      }
-
-      /// <summary>
-      /// Gets or sets whether to track linked entries. Disabled by default.
-      /// </summary>
-      /// <remarks>Prior to .NET 7 this feature was always enabled.</remarks>
-      public bool TrackLinkedCacheEntries { get; set; }
-
-      MemoryCacheOptions IOptions<MemoryCacheOptions>.Value
-      {
-         get { return this; }
-      }
-   }
-}

+ 0 - 24
frameworks/CSharp/appmpower/src/Microsoft/CachKey.cs

@@ -1,24 +0,0 @@
-using System;
-
-namespace PlatformBenchmarks
-{
-   public sealed class CacheKey : IEquatable<CacheKey>
-   {
-      private readonly int _value;
-
-      public CacheKey(int value)
-          => _value = value;
-
-      public bool Equals(CacheKey key)
-          => key._value == _value;
-
-      public override bool Equals(object obj)
-          => ReferenceEquals(obj, this);
-
-      public override int GetHashCode()
-          => _value;
-
-      public override string ToString()
-          => _value.ToString();
-   }
-}

+ 0 - 59
frameworks/CSharp/appmpower/src/Microsoft/StringBuilderCache.cs

@@ -1,59 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved. 
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
-
-using System;
-using System.Text;
-
-namespace PlatformBenchmarks
-{
-   internal static class StringBuilderCache
-   {
-      private const int DefaultCapacity = 1386;
-      private const int MaxBuilderSize = DefaultCapacity * 3;
-
-      [ThreadStatic]
-      private static StringBuilder t_cachedInstance;
-
-      /// <summary>Get a StringBuilder for the specified capacity.</summary>
-      /// <remarks>If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied.</remarks>
-      public static StringBuilder Acquire(int capacity = DefaultCapacity)
-      {
-         if (capacity <= MaxBuilderSize)
-         {
-            StringBuilder sb = t_cachedInstance;
-            if (capacity < DefaultCapacity)
-            {
-               capacity = DefaultCapacity;
-            }
-
-            if (sb != null)
-            {
-               // Avoid stringbuilder block fragmentation by getting a new StringBuilder
-               // when the requested size is larger than the current capacity
-               if (capacity <= sb.Capacity)
-               {
-                  t_cachedInstance = null;
-                  sb.Clear();
-                  return sb;
-               }
-            }
-         }
-         return new StringBuilder(capacity);
-      }
-
-      public static void Release(StringBuilder sb)
-      {
-         if (sb.Capacity <= MaxBuilderSize)
-         {
-            t_cachedInstance = sb;
-         }
-      }
-
-      public static string GetStringAndRelease(StringBuilder sb)
-      {
-         string result = sb.ToString();
-         Release(sb);
-         return result;
-      }
-   }
-}

+ 0 - 42
frameworks/CSharp/appmpower/src/Program.cs

@@ -1,42 +0,0 @@
-using System;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Hosting.Server.Features;
-using Microsoft.AspNetCore.Server.Kestrel.Core;
-using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
-using Microsoft.Extensions.Logging.Abstractions;
-using Microsoft.Extensions.Options;
-
-namespace appMpower
-{
-   class Program
-   {
-      static async Task Main(string[] args)
-      {
-         var socketTransportOptions = new SocketTransportOptions();
-         var socketTransportFactory = new SocketTransportFactory(Options.Create(socketTransportOptions), NullLoggerFactory.Instance);
-         var kestrelServerOptions = new KestrelServerOptions();
-
-         kestrelServerOptions.Listen(IPAddress.Any, 8080);
-         kestrelServerOptions.AllowSynchronousIO = true; 
-         kestrelServerOptions.AddServerHeader = false;
-
-         using var kestrelServer = new KestrelServer(Options.Create(kestrelServerOptions), socketTransportFactory, NullLoggerFactory.Instance);
-
-         await kestrelServer.StartAsync(new HttpApplication(), CancellationToken.None);
-
-         Console.WriteLine("Listening on:");
-
-         foreach (var address in kestrelServer.Features.Get<IServerAddressesFeature>().Addresses)
-         {
-            Console.WriteLine(" - " + address);
-         }
-
-         Console.WriteLine("Process CTRL+C to quit");
-         var wh = new ManualResetEventSlim();
-         Console.CancelKeyPress += (sender, e) => wh.Set();
-         wh.Wait();
-      }
-   }
-}

+ 10 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Constants.cs

@@ -0,0 +1,10 @@
+using appMpower.Orm.Data; 
+
+namespace appMpower.Orm
+{
+  public static class Constants
+  {
+    public static Dbms Dbms = Dbms.PostgreSQL; 
+    public static DbProvider DbProvider = DbProvider.ODBC; 
+  }
+}

+ 40 - 63
frameworks/CSharp/appmpower/src/Data/DbCommand.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbCommand.cs

@@ -1,56 +1,40 @@
 using System.Data;
 using System.Data;
-using System.Threading.Tasks;
+using System.Data.Odbc; 
 
 
-namespace appMpower.Data
+namespace appMpower.Orm.Data
 {
 {
    public class DbCommand : IDbCommand
    public class DbCommand : IDbCommand
    {
    {
-      private IDbCommand _dbCommand;
+      private OdbcCommand _odbcCommand;
       private DbConnection _dbConnection;
       private DbConnection _dbConnection;
 
 
       public DbCommand(DbConnection dbConnection)
       public DbCommand(DbConnection dbConnection)
       {
       {
-         _dbCommand = dbConnection.CreateCommand();
+         _odbcCommand = (OdbcCommand)dbConnection.CreateCommand();
          _dbConnection = dbConnection;
          _dbConnection = dbConnection;
       }
       }
 
 
       public DbCommand(string commandText, DbConnection dbConnection)
       public DbCommand(string commandText, DbConnection dbConnection)
       {
       {
-         dbConnection.GetCommand(commandText, CommandType.Text, this);
+         _odbcCommand = dbConnection.GetCommand(commandText, CommandType.Text);
+         _dbConnection = dbConnection;
       }
       }
 
 
       public DbCommand(string commandText, CommandType commandType, DbConnection dbConnection)
       public DbCommand(string commandText, CommandType commandType, DbConnection dbConnection)
       {
       {
-         dbConnection.GetCommand(commandText, commandType, this);
-      }
-
-      internal DbCommand(IDbCommand dbCommand, DbConnection dbConnection)
-      {
-         _dbCommand = dbCommand;
-         _dbConnection = dbConnection;
+         _odbcCommand = dbConnection.GetCommand(commandText, commandType);
+         _dbConnection = dbConnection; 
       }
       }
 
 
       internal IDbCommand Command
       internal IDbCommand Command
       {
       {
          get
          get
          {
          {
-            return _dbCommand;
+            return _odbcCommand;
          }
          }
          set
          set
          {
          {
-            _dbCommand = value;
-         }
-      }
-
-      internal DbConnection DbConnection
-      {
-         get
-         {
-            return _dbConnection;
-         }
-         set
-         {
-            _dbConnection = value;
+            _odbcCommand = (OdbcCommand)value;
          }
          }
       }
       }
 
 
@@ -58,11 +42,11 @@ namespace appMpower.Data
       {
       {
          get
          get
          {
          {
-            return _dbCommand.CommandText;
+            return _odbcCommand.CommandText;
          }
          }
          set
          set
          {
          {
-            _dbCommand.CommandText = value;
+            _odbcCommand.CommandText = value;
          }
          }
       }
       }
 
 
@@ -70,22 +54,22 @@ namespace appMpower.Data
       {
       {
          get
          get
          {
          {
-            return _dbCommand.CommandTimeout;
+            return _odbcCommand.CommandTimeout;
          }
          }
          set
          set
          {
          {
-            _dbCommand.CommandTimeout = value;
+            _odbcCommand.CommandTimeout = value;
          }
          }
       }
       }
       public CommandType CommandType
       public CommandType CommandType
       {
       {
          get
          get
          {
          {
-            return _dbCommand.CommandType;
+            return _odbcCommand.CommandType;
          }
          }
          set
          set
          {
          {
-            _dbCommand.CommandType = value;
+            _odbcCommand.CommandType = value;
          }
          }
       }
       }
 
 
@@ -94,11 +78,11 @@ namespace appMpower.Data
       {
       {
          get
          get
          {
          {
-            return _dbCommand.Connection;
+            return _odbcCommand.Connection;
          }
          }
          set
          set
          {
          {
-            _dbCommand.Connection = (IDbConnection?)value;
+            _odbcCommand.Connection = (OdbcConnection?)value;
          }
          }
       }
       }
 #nullable disable
 #nullable disable
@@ -107,7 +91,7 @@ namespace appMpower.Data
       {
       {
          get
          get
          {
          {
-            return _dbCommand.Parameters;
+            return _odbcCommand.Parameters;
          }
          }
       }
       }
 
 
@@ -116,11 +100,11 @@ namespace appMpower.Data
       {
       {
          get
          get
          {
          {
-            return _dbCommand.Transaction;
+            return _odbcCommand.Transaction;
          }
          }
          set
          set
          {
          {
-            _dbCommand.Transaction = (IDbTransaction?)value;
+            _odbcCommand.Transaction = (OdbcTransaction?)value;
          }
          }
       }
       }
 #nullable disable
 #nullable disable
@@ -129,21 +113,21 @@ namespace appMpower.Data
       {
       {
          get
          get
          {
          {
-            return _dbCommand.UpdatedRowSource;
+            return _odbcCommand.UpdatedRowSource;
          }
          }
          set
          set
          {
          {
-            _dbCommand.UpdatedRowSource = value;
+            _odbcCommand.UpdatedRowSource = value;
          }
          }
       }
       }
       public void Cancel()
       public void Cancel()
       {
       {
-         _dbCommand.Cancel();
+         _odbcCommand.Cancel();
       }
       }
 
 
       public IDbDataParameter CreateParameter()
       public IDbDataParameter CreateParameter()
       {
       {
-         return _dbCommand.CreateParameter();
+         return _odbcCommand.CreateParameter();
       }
       }
 
 
       public IDbDataParameter CreateParameter(string name, object value)
       public IDbDataParameter CreateParameter(string name, object value)
@@ -153,21 +137,21 @@ namespace appMpower.Data
 
 
       public IDbDataParameter CreateParameter(string name, DbType dbType, object value)
       public IDbDataParameter CreateParameter(string name, DbType dbType, object value)
       {
       {
-         IDbDataParameter dbDataParameter = null;
+         IDbDataParameter dbDataParameter;
 
 
-         if (this.Parameters.Contains(name))
+         if (_odbcCommand.Parameters.Contains(name))
          {
          {
-            dbDataParameter = this.Parameters[name] as IDbDataParameter;
+            dbDataParameter = _odbcCommand.Parameters[name];
             dbDataParameter.Value = value;
             dbDataParameter.Value = value;
          }
          }
          else
          else
          {
          {
-            dbDataParameter = _dbCommand.CreateParameter();
+            dbDataParameter = _odbcCommand.CreateParameter();
 
 
             dbDataParameter.ParameterName = name;
             dbDataParameter.ParameterName = name;
             dbDataParameter.DbType = dbType;
             dbDataParameter.DbType = dbType;
             dbDataParameter.Value = value;
             dbDataParameter.Value = value;
-            this.Parameters.Add(dbDataParameter);
+            _odbcCommand.Parameters.Add(dbDataParameter);
          }
          }
 
 
          return dbDataParameter;
          return dbDataParameter;
@@ -175,51 +159,44 @@ namespace appMpower.Data
 
 
       public int ExecuteNonQuery()
       public int ExecuteNonQuery()
       {
       {
-         return _dbCommand.ExecuteNonQuery();
+         return _odbcCommand.ExecuteNonQuery();
       }
       }
 
 
       public IDataReader ExecuteReader()
       public IDataReader ExecuteReader()
       {
       {
-         return _dbCommand.ExecuteReader();
+         return _odbcCommand.ExecuteReader();
       }
       }
 
 
       public async Task<int> ExecuteNonQueryAsync()
       public async Task<int> ExecuteNonQueryAsync()
       {
       {
-         return await (_dbCommand as System.Data.Common.DbCommand).ExecuteNonQueryAsync();
-      }
-
-      public async Task<System.Data.Common.DbDataReader> ExecuteReaderAsync(CommandBehavior behavior)
-      {
-         return await (_dbCommand as System.Data.Common.DbCommand).ExecuteReaderAsync(behavior);
+         return await _odbcCommand.ExecuteNonQueryAsync();
       }
       }
 
 
       public IDataReader ExecuteReader(CommandBehavior behavior)
       public IDataReader ExecuteReader(CommandBehavior behavior)
       {
       {
-         return _dbCommand.ExecuteReader(behavior);
+         return _odbcCommand.ExecuteReader(behavior);
       }
       }
 
 
-#nullable enable
-      public object? ExecuteScalar()
+      public async Task<System.Data.Common.DbDataReader> ExecuteReaderAsync(CommandBehavior behavior)
       {
       {
-         return _dbCommand.ExecuteScalar();
+         return await _odbcCommand.ExecuteReaderAsync(behavior);
       }
       }
-#nullable disable
 
 
 #nullable enable
 #nullable enable
-      public async Task<object?> ExecuteScalarAsync()
+      public object? ExecuteScalar()
       {
       {
-         return await ((System.Data.Common.DbCommand)_dbCommand).ExecuteScalarAsync();
+         return _odbcCommand.ExecuteScalar();
       }
       }
 #nullable disable
 #nullable disable
 
 
       public void Prepare()
       public void Prepare()
       {
       {
-         _dbCommand.Prepare();
+         _odbcCommand.Prepare();
       }
       }
 
 
       public void Dispose()
       public void Dispose()
       {
       {
-         _dbConnection.ReleaseCommand(this);
+         _dbConnection.Release(_odbcCommand);
       }
       }
    }
    }
 }
 }

+ 174 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnection.cs

@@ -0,0 +1,174 @@
+using System.Collections.Concurrent;
+using System.Data;
+using System.Data.Odbc;
+
+namespace appMpower.Orm.Data
+{
+   public class DbConnection : IDbConnection
+   {
+      private string _connectionString;
+      private bool _keyed = false;
+      private int _number;
+      private OdbcConnection _odbcConnection;
+      private ConcurrentStack<OdbcCommand> _odbcCommands = new();
+      private Dictionary<string, OdbcCommand> _keyedOdbcCommands;
+
+      public DbConnection()
+      {
+      }
+
+      public DbConnection(string connectionString, bool keyed = false)
+      {
+         _keyed = keyed;
+         _connectionString = connectionString; 
+         GetConnection();
+      }
+
+      public IDbConnection Connection
+      {
+         get
+         {
+            return _odbcConnection;
+         }
+         set
+         {
+            _odbcConnection = (OdbcConnection)value;
+         }
+      }
+
+      public string ConnectionString
+      {
+         get
+         {
+            return _odbcConnection.ConnectionString;
+         }
+         set
+         {
+            _connectionString = value; 
+            GetConnection();
+         }
+      }
+
+      private void GetConnection()
+      {
+         if (_keyed)
+         {
+            (_number, _odbcConnection, _keyedOdbcCommands) = 
+               DbConnectionsKeyed.GetConnectionBase(_connectionString).GetAwaiter().GetResult();
+         }
+         else
+         {
+            (_number, _odbcConnection, _odbcCommands) = 
+               DbConnections.GetConnectionBase(_connectionString).GetAwaiter().GetResult();
+         }
+      }
+
+      public int ConnectionTimeout
+      {
+         get
+         {
+            return _odbcConnection.ConnectionTimeout;
+         }
+      }
+
+      public string Database
+      {
+         get
+         {
+            return _odbcConnection.Database;
+         }
+      }
+
+      public ConnectionState State
+      {
+         get
+         {
+            if (_odbcConnection is null) return ConnectionState.Closed;
+            return _odbcConnection.State;
+         }
+      }
+
+      public IDbTransaction BeginTransaction()
+      {
+         return _odbcConnection.BeginTransaction();
+      }
+
+      public IDbTransaction BeginTransaction(IsolationLevel il)
+      {
+         return _odbcConnection.BeginTransaction(il);
+      }
+
+      public void ChangeDatabase(string databaseName)
+      {
+         _odbcConnection.ChangeDatabase(databaseName);
+      }
+
+      public void Close()
+      {
+         _odbcConnection.Close();
+      }
+
+      public IDbCommand CreateCommand()
+      {
+         return _odbcConnection.CreateCommand();
+      }
+
+      public void Open()
+      {
+         if (_odbcConnection.State == ConnectionState.Closed)
+         {
+            _odbcConnection.Open();
+         }
+      }
+
+      public async Task OpenAsync()
+      {
+         if (_odbcConnection.State == ConnectionState.Closed)
+         {
+            await _odbcConnection.OpenAsync();
+         }
+      }
+
+      public void Dispose()
+      {
+         if (_keyed)
+         {
+            DbConnectionsKeyed.Release((Number: _number, OdbcConnection: _odbcConnection, KeyedOdbcCommands: _keyedOdbcCommands));
+         }
+         else
+         {
+            DbConnections.Release((Number: _number, OdbcConnection: _odbcConnection, OdbcCommands: _odbcCommands));
+         }
+      }
+
+      internal OdbcCommand GetCommand(string commandText, CommandType commandType)
+      {
+         OdbcCommand odbcCommand;
+
+         if (_odbcCommands.TryPop(out odbcCommand))
+         {
+            if (commandText != odbcCommand.CommandText)
+            {
+               odbcCommand.CommandText = commandText; 
+               odbcCommand.Parameters.Clear();
+            }
+
+            return odbcCommand; 
+         }
+         else if (_keyed && _keyedOdbcCommands.TryGetValue(commandText, out odbcCommand)) return odbcCommand; 
+
+         odbcCommand = _odbcConnection.CreateCommand();
+         odbcCommand.CommandText = commandText;
+         odbcCommand.CommandType = commandType;
+         odbcCommand.Prepare();
+
+         return odbcCommand;
+      }
+
+      internal void Release(OdbcCommand odbcCommand)
+      {
+         if (_keyed) _keyedOdbcCommands.TryAdd(odbcCommand.CommandText, odbcCommand);
+         else _odbcCommands.Push(odbcCommand);
+      }
+   }
+}

+ 61 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnections.cs

@@ -0,0 +1,61 @@
+using System.Collections.Concurrent;
+using System.Data.Odbc;
+
+namespace appMpower.Orm.Data
+{
+   internal static class DbConnections
+   {
+      private static bool _maxConnectionsCreated = false;
+      private static short _createdConnections = 0;
+      private static short _maxConnections = 500;
+
+      private static ConcurrentStack<(int Number, OdbcConnection OdbcConnection, ConcurrentStack<OdbcCommand> OdbcCommands)> _connectionsStack = new();
+      private static ConcurrentQueue<TaskCompletionSource<(int Number, OdbcConnection OdbcConnection, ConcurrentStack<OdbcCommand> OdbcCommands)>> _waitingQueue = new();
+      
+      internal static async Task<(int Number, OdbcConnection OdbcConnection, ConcurrentStack<OdbcCommand> OdbcCommands)> GetConnectionBase(string connectionString)
+      {
+         (int Number, OdbcConnection OdbcConnection, ConcurrentStack<OdbcCommand> OdbcCommands) dbConnectionBase;
+
+         if (!_connectionsStack.TryPop(out dbConnectionBase))
+         {
+            if (_maxConnectionsCreated)
+            {
+               dbConnectionBase = await GetDbConnectionBaseAsync();
+            }
+            else
+            {
+               _createdConnections++;
+               dbConnectionBase = (Number: _maxConnections, OdbcConnection: new OdbcConnection(connectionString), OdbcCommands: new ConcurrentStack<OdbcCommand>());
+
+               if (_createdConnections == _maxConnections) _maxConnectionsCreated = true;
+
+               //Console.WriteLine("opened connection number: " + dbConnectionBase._number);
+            }
+         }
+
+         return dbConnectionBase;
+      }
+
+      internal static void Release((int Number, OdbcConnection OdbcConnection, ConcurrentStack<OdbcCommand> OdbcCommands) dbConnectionBase)
+      {
+         TaskCompletionSource<(int Number, OdbcConnection OdbcConnection, ConcurrentStack<OdbcCommand> OdbcCommands)> taskCompletionSource;
+
+         if (_waitingQueue.TryDequeue(out taskCompletionSource))
+         {
+            taskCompletionSource.SetResult(dbConnectionBase);
+         }
+         else
+         {
+            _connectionsStack.Push(dbConnectionBase);
+         }
+      }
+
+      private static Task<(int Number, OdbcConnection OdbcConnection, ConcurrentStack<OdbcCommand> OdbcCommands)> GetDbConnectionBaseAsync()
+      {
+         var taskCompletionSource = new TaskCompletionSource<(int Number, OdbcConnection OdbcConnection, ConcurrentStack<OdbcCommand> OdbcCommands)>(TaskCreationOptions.RunContinuationsAsynchronously);
+
+         _waitingQueue.Enqueue(taskCompletionSource);
+         return taskCompletionSource.Task;
+      }
+   }
+}

+ 61 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbConnectionsKeyed.cs

@@ -0,0 +1,61 @@
+using System.Collections.Concurrent;
+using System.Data.Odbc;
+
+namespace appMpower.Orm.Data
+{
+   internal static class DbConnectionsKeyed
+   {
+      private static bool _maxConnectionsCreated = false;
+      private static short _createdConnections = 0;
+      private static short _maxConnections = 500;
+
+      private static ConcurrentStack<(int Number, OdbcConnection OdbcConnection, Dictionary<string, OdbcCommand>)> _connectionsStack = new();
+      private static ConcurrentQueue<TaskCompletionSource<(int Number, OdbcConnection OdbcConnection, Dictionary<string, OdbcCommand>)>> _waitingQueue = new();
+
+      internal static async Task<(int Number, OdbcConnection OdbcConnection, Dictionary<string, OdbcCommand> KeyedOdbcCommands)> GetConnectionBase(string connectionString)
+      {
+         (int Number, OdbcConnection OdbcConnection, Dictionary<string, OdbcCommand> KeyedOdbcCommands) dbConnectionBase;
+
+         if (!_connectionsStack.TryPop(out dbConnectionBase))
+         {
+            if (_maxConnectionsCreated)
+            {
+               dbConnectionBase = await GetDbConnectionBaseAsync();
+            }
+            else
+            {
+               _createdConnections++;
+               dbConnectionBase = (Number: _maxConnections, OdbcConnection: new OdbcConnection(connectionString), KeyedOdbcCommands: new Dictionary<string, OdbcCommand>());
+
+               if (_createdConnections == _maxConnections) _maxConnectionsCreated = true;
+
+               //Console.WriteLine("opened connection number: " + dbConnectionBase._number);
+            }
+         }
+
+         return dbConnectionBase;
+      }
+
+      internal static void Release((int Number, OdbcConnection OdbcConnection, Dictionary<string, OdbcCommand> KeyedOdbcCommands) dbConnectionBase)
+      {
+         TaskCompletionSource<(int Number, OdbcConnection OdbcConnection, Dictionary<string, OdbcCommand>)> taskCompletionSource;
+
+         if (_waitingQueue.TryDequeue(out taskCompletionSource))
+         {
+            taskCompletionSource.SetResult(dbConnectionBase);
+         }
+         else
+         {
+            _connectionsStack.Push(dbConnectionBase);
+         }
+      }
+
+      private static Task<(int Number, OdbcConnection OdbcConnection, Dictionary<string, OdbcCommand>)> GetDbConnectionBaseAsync()
+      {
+         var taskCompletionSource = new TaskCompletionSource<(int Number, OdbcConnection OdbcConnection, Dictionary<string, OdbcCommand>)>(TaskCreationOptions.RunContinuationsAsynchronously);
+
+         _waitingQueue.Enqueue(taskCompletionSource);
+         return taskCompletionSource.Task;
+      }
+   }
+}

+ 8 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProvider.cs

@@ -0,0 +1,8 @@
+namespace appMpower.Orm.Data
+{
+   public enum DbProvider
+   {
+      ADO = 0,
+      ODBC = 1,
+   }
+}

+ 21 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Data/DbProviderFactory.cs

@@ -0,0 +1,21 @@
+using System.Data;
+
+namespace appMpower.Orm.Data
+{
+   public static class DbProviderFactory
+   {
+      public static string ConnectionString; 
+
+      public static void SetConnectionString()
+      {
+         if (Constants.Dbms == Dbms.MySQL)
+         {
+            ConnectionString = "Driver={MariaDB};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;Pooling=false;OPTIONS=67108864;FLAG_FORWARD_CURSOR=1;sslmode=DISABLED;CharSet=utf8;"; 
+         }
+         else
+         {
+            ConnectionString = "Driver={PostgreSQL};Server=tfb-database;Database=hello_world;Uid=benchmarkdbuser;Pwd=benchmarkdbpass;UseServerSidePrepare=1;Pooling=false;sslmode=disable";
+         }
+      }
+   }
+}

+ 9 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Data/Dbms.cs

@@ -0,0 +1,9 @@
+namespace appMpower.Orm.Data
+{
+   public enum Dbms
+   {
+      MySQL = 0,
+      PostgreSQL = 1,
+      SQLServer = 2,
+   }
+}

+ 66 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/DotnetMethods.cs

@@ -0,0 +1,66 @@
+using System.Runtime.InteropServices;
+using System.Text; 
+using System.Text.Json; 
+using appMpower.Orm.Data; 
+using appMpower.Orm.Objects; 
+using appMpower.Orm.Serializers; 
+
+namespace appMpower.Orm;
+
+//These methods are for test purposes only; not used in actual execution
+public static class DotnetMethods
+{
+    private static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions
+    {
+        Indented = false, 
+        SkipValidation = true
+    };
+
+    private readonly static WorldSerializer _worldSerializer = new WorldSerializer();
+    private readonly static WorldsSerializer _worldsSerializer = new WorldsSerializer();
+
+    public static byte[] Db()
+    {
+        var world = RawDb.LoadSingleQueryRow().GetAwaiter().GetResult();
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldSerializer.Serialize(utf8JsonWriter, world);
+
+        return memoryStream.ToArray();
+    }
+
+    public static byte[] Query(int queries)
+    {
+        World[] worlds = RawDb.ReadMultipleRows(queries).GetAwaiter().GetResult();
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldsSerializer.Serialize(utf8JsonWriter, worlds);
+
+        return memoryStream.ToArray();
+    }
+
+    public static byte[] Updates(int count)
+    {
+        World[] worlds = RawDb.LoadMultipleUpdatesRows(count).GetAwaiter().GetResult();
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldsSerializer.Serialize(utf8JsonWriter, worlds);
+
+        return memoryStream.ToArray();
+    }
+
+    public static byte[] Fortunes()
+    {
+        List<Fortune> fortunes = RawDb.LoadFortunesRows().GetAwaiter().GetResult(); 
+        string fortunesView = FortunesView.Render(fortunes);
+        byte[] byteArray = Encoding.UTF8.GetBytes(fortunesView);
+
+        return byteArray.ToArray();
+    }
+}

+ 4 - 17
frameworks/CSharp/appmpower/src/FortunesView.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/FortunesView.cs

@@ -1,33 +1,23 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.IO.Pipelines;
 using System.Globalization;
 using System.Globalization;
 using System.Text;
 using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using System.Web;
 using System.Web;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Primitives;
 using PlatformBenchmarks;
 using PlatformBenchmarks;
+using appMpower.Orm.Objects;
 
 
-namespace appMpower
+namespace appMpower.Orm
 {
 {
    public static class FortunesView
    public static class FortunesView
    {
    {
-      private readonly static KeyValuePair<string, StringValues> _headerServer =
-         new KeyValuePair<string, StringValues>("Server", "k");
-      private readonly static KeyValuePair<string, StringValues> _headerContentType =
-         new KeyValuePair<string, StringValues>("Content-Type", "text/html; charset=UTF-8");
-
       public static char[] _fortunesTableStart = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>".ToCharArray();
       public static char[] _fortunesTableStart = "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>".ToCharArray();
       public static char[] _fortunesRowStart = "<tr><td>".ToCharArray();
       public static char[] _fortunesRowStart = "<tr><td>".ToCharArray();
       public static char[] _fortunesColumn = "</td><td>".ToCharArray();
       public static char[] _fortunesColumn = "</td><td>".ToCharArray();
       public static char[] _fortunesRowEnd = "</td></tr>".ToCharArray();
       public static char[] _fortunesRowEnd = "</td></tr>".ToCharArray();
       public static char[] _fortunesTableEnd = "</table></body></html>".ToCharArray();
       public static char[] _fortunesTableEnd = "</table></body></html>".ToCharArray();
 
 
-      public static async Task Render(IHeaderDictionary headerDictionary, PipeWriter pipeWriter, List<Fortune> fortunes)
+      public static string Render(List<Fortune> fortunes)
       {
       {
-         headerDictionary.Add(_headerServer);
-         headerDictionary.Add(_headerContentType);
-
          var writer = StringBuilderCache.Acquire();
          var writer = StringBuilderCache.Acquire();
 
 
          writer.Append(_fortunesTableStart);
          writer.Append(_fortunesTableStart);
@@ -39,10 +29,7 @@ namespace appMpower
 
 
          writer.Append(_fortunesTableEnd);
          writer.Append(_fortunesTableEnd);
 
 
-         headerDictionary.Add(new KeyValuePair<string, StringValues>("Content-Length", (writer.Length + 32).ToString()));
-
-         await pipeWriter.WriteAsync(Encoding.UTF8.GetBytes(StringBuilderCache.GetStringAndRelease(writer)));
-         pipeWriter.Complete();
+         return StringBuilderCache.GetStringAndRelease(writer);
       }
       }
    }
    }
 }
 }

+ 19 - 37
frameworks/CSharp/appmpower/src/Microsoft/BatchUpdateString.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/BatchUpdateString.cs

@@ -1,7 +1,8 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
 
-using System.Linq;
+using appMpower.Orm; 
+using appMpower.Orm.Data; 
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {
@@ -33,51 +34,32 @@ namespace PlatformBenchmarks
          //sb.Append("(?::int,?::int)) AS temp(id, randomNumber) WHERE temp.id = world.id");
          //sb.Append("(?::int,?::int)) AS temp(id, randomNumber) WHERE temp.id = world.id");
          */
          */
 
 
-#if MYSQL
-         for (int i = 0; i < batchSize; i++)
+         if (Constants.Dbms == Dbms.MySQL)
          {
          {
-            sb.Append("UPDATE world SET randomNumber=? WHERE id=?;");
+            for (int i = 0; i < batchSize; i++)
+            {
+               sb.Append("UPDATE world SET randomNumber=? WHERE id=?;");
+            }
          }
          }
-#elif ADO
-         /*
-         sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ");
-         Enumerable.Range(0, lastIndex).ToList().ForEach(i => sb.Append($"(@i{i}, @r{i}), "));
-         sb.Append($"(@i{lastIndex}, @r{lastIndex}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id");
-         */
-
-         sb.Append("UPDATE world SET randomNumber=CASE id ");
-
-         for (int i = 0; i < batchSize; i++)
-         {
-            sb.Append("WHEN @i" + i + " THEN @r" + i + " ");
-         }
-
-         sb.Append("ELSE randomnumber END WHERE id IN(");
-
-         for (int i = 0; i < lastIndex; i++)
+         else
          {
          {
-            sb.Append("@j" + i + ",");
-         }
+            sb.Append("UPDATE world SET randomNumber=CASE id ");
 
 
-         sb.Append("@j" + lastIndex + ")");
-#else
-         sb.Append("UPDATE world SET randomNumber=CASE id ");
+            for (int i = 0; i < batchSize; i++)
+            {
+               sb.Append("WHEN ? THEN ? ");
+            }
 
 
-         for (int i = 0; i < batchSize; i++)
-         {
-            sb.Append("WHEN ? THEN ? ");
-         }
+            sb.Append("ELSE randomnumber END WHERE id IN(");
 
 
-         sb.Append("ELSE randomnumber END WHERE id IN(");
+            for (int i = 0; i < lastIndex; i++)
+            {
+               sb.Append("?,");
+            }
 
 
-         for (int i = 0; i < lastIndex; i++)
-         {
-            sb.Append("?,");
+            sb.Append("?)");
          }
          }
 
 
-         sb.Append("?)");
-#endif
-
          return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb);
          return _queries[batchSize] = StringBuilderCache.GetStringAndRelease(sb);
       }
       }
    }
    }

+ 0 - 2
frameworks/CSharp/appmpower/src/Microsoft/ConcurrentRandom.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/ConcurrentRandom.cs

@@ -1,9 +1,7 @@
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Copyright (c) .NET Foundation. All rights reserved. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 
 
 
-using System;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
-using System.Threading;
 
 
 namespace PlatformBenchmarks
 namespace PlatformBenchmarks
 {
 {

+ 54 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Microsoft/StringBuilderCache.cs

@@ -0,0 +1,54 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Text;
+
+namespace PlatformBenchmarks; 
+
+internal static class StringBuilderCache
+{
+    private const int DefaultCapacity = 1386;
+    private const int MaxBuilderSize = DefaultCapacity * 3;
+    [ThreadStatic]
+    private static StringBuilder t_cachedInstance;
+
+    public static StringBuilder Acquire(int capacity = DefaultCapacity)
+    {
+        if (capacity <= MaxBuilderSize)
+        {
+            StringBuilder sb = t_cachedInstance;
+
+            if (capacity < DefaultCapacity)
+            {
+                capacity = DefaultCapacity;
+            }
+
+            if (sb != null)
+            {
+                if (capacity <= sb.Capacity)
+                {
+                    t_cachedInstance = null;
+                    sb.Clear();
+                    return sb;
+                }
+            }
+        }
+
+        return new StringBuilder(capacity);
+    }
+
+    public static void Release(StringBuilder sb)
+    {
+        if (sb.Capacity <= MaxBuilderSize)
+        {
+            t_cachedInstance = sb;
+        }
+    }
+
+    public static string GetStringAndRelease(StringBuilder sb)
+    {
+        string result = sb.ToString();
+        Release(sb);
+        return result;
+    }
+}

+ 203 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs

@@ -0,0 +1,203 @@
+using System.Runtime.InteropServices;
+using System.Text; 
+using System.Text.Json; 
+using appMpower.Orm.Data; 
+using appMpower.Orm.Objects; 
+using appMpower.Orm.Serializers; 
+
+namespace appMpower.Orm;
+
+public static class NativeMethods
+{
+    private static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions
+    {
+        Indented = false, 
+        SkipValidation = true
+    };
+
+    private readonly static WorldSerializer _worldSerializer = new WorldSerializer();
+    private readonly static WorldsSerializer _worldsSerializer = new WorldsSerializer();
+    private readonly static FortunesSerializer _fortunesSerializer = new FortunesSerializer();
+    private static readonly byte[] _delimiter = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF };
+
+
+    [UnmanagedCallersOnly(EntryPoint = "Dbms")]
+    public static void Dbms(int dbms)
+    {
+        Constants.Dbms = (Dbms)dbms; 
+        DbProviderFactory.SetConnectionString();
+    }
+
+    [UnmanagedCallersOnly(EntryPoint = "DbProvider")]
+    public static void DbProvider(int dbProvider)
+    {
+        Constants.DbProvider = (DbProvider)dbProvider; 
+        DbProviderFactory.SetConnectionString();
+    }
+    
+    [UnmanagedCallersOnly(EntryPoint = "FreeHandlePointer")]
+    public static void FreeHandlePointer(IntPtr handlePointer)
+    {
+        GCHandle handle = GCHandle.FromIntPtr(handlePointer);
+        handle.Free();
+    }
+
+    [UnmanagedCallersOnly(EntryPoint = "Db")]
+    public static unsafe IntPtr Db(int* length, IntPtr* handlePointer)
+    {
+        var world = RawDb.LoadSingleQueryRow().GetAwaiter().GetResult();
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldSerializer.Serialize(utf8JsonWriter, world);
+
+        *length = (int)utf8JsonWriter.BytesCommitted; 
+        byte[] byteArray = memoryStream.ToArray();
+
+        GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
+        // return the managed and byteArrayPointer pointer
+        IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
+        *handlePointer = GCHandle.ToIntPtr(handle);
+
+        return byteArrayPointer;
+        /*
+        fixed(byte* b = memoryStream.ToArray())
+        {
+            return b; 
+        }
+        */
+    }
+
+    /*
+    [UnmanagedCallersOnly(EntryPoint = "Fortunes")]
+    public static unsafe IntPtr Fortunes(int* length, IntPtr* handlePointer)
+    {
+        List<Fortune> fortunes = RawDb.LoadFortunesRows().GetAwaiter().GetResult(); 
+        string fortunesView = FortunesView.Render(fortunes);
+        byte[] byteArray = Encoding.UTF8.GetBytes(fortunesView);
+
+        *length = byteArray.Length; 
+
+        GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
+        IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
+        *handlePointer = GCHandle.ToIntPtr(handle);
+
+        return byteArrayPointer;
+    }
+    */
+
+    [UnmanagedCallersOnly(EntryPoint = "Fortunes")]
+    public static unsafe IntPtr Fortunes(int* length, IntPtr* handlePointer)
+    {
+        List<Fortune> fortunes = RawDb.LoadFortunesRows().GetAwaiter().GetResult(); 
+
+        int totalSize = 0;
+
+        foreach (var fortune in fortunes)
+        {
+            totalSize += sizeof(int) // for Id
+                       + Encoding.UTF8.GetByteCount(fortune.Message ?? "") // for Message
+                       + _delimiter.Length; // for delimiter
+        }
+
+        // Allocate the total buffer
+        byte[] buffer = new byte[totalSize];
+        int offset = 0;
+
+        // Write each object to the buffer
+        foreach (var fortune in fortunes)
+        {
+            // Write Id
+            BitConverter.TryWriteBytes(buffer.AsSpan(offset, sizeof(int)), fortune.Id);
+            offset += sizeof(int);
+
+            // Write Message
+            int descriptionLength = Encoding.UTF8.GetBytes(fortune.Message ?? "", buffer.AsSpan(offset));
+            offset += descriptionLength;
+
+            // Write Delimiter
+            _delimiter.CopyTo(buffer, offset);
+            offset += _delimiter.Length;
+        }
+
+        byte[] byteArray = buffer.ToArray();
+        *length = byteArray.Length; 
+
+        /*
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _fortunesSerializer.Serialize(utf8JsonWriter, fortunes);
+
+        byte[] byteArray = memoryStream.ToArray();
+        *length = (int)utf8JsonWriter.BytesCommitted; 
+        */
+
+        GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
+        IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
+        *handlePointer = GCHandle.ToIntPtr(handle);
+
+        return byteArrayPointer;
+    }
+
+    [UnmanagedCallersOnly(EntryPoint = "Query")]
+    public static unsafe IntPtr Query(int queries, int* length, IntPtr* handlePointer)
+    {
+        World[] worlds = RawDb.ReadMultipleRows(queries).GetAwaiter().GetResult();
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldsSerializer.Serialize(utf8JsonWriter, worlds);
+
+        *length = (int)utf8JsonWriter.BytesCommitted; 
+        byte[] byteArray = memoryStream.ToArray();
+
+        GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
+        IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
+        *handlePointer = GCHandle.ToIntPtr(handle);
+
+        return byteArrayPointer;
+    }
+
+    [UnmanagedCallersOnly(EntryPoint = "Updates")]
+    public static unsafe IntPtr Updates(int count, int* length, IntPtr* handlePointer)
+    {
+        World[] worlds = RawDb.LoadMultipleUpdatesRows(count).GetAwaiter().GetResult();
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldsSerializer.Serialize(utf8JsonWriter, worlds);
+
+        *length = (int)utf8JsonWriter.BytesCommitted; 
+        byte[] byteArray = memoryStream.ToArray();
+
+        GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
+        IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
+        *handlePointer = GCHandle.ToIntPtr(handle);
+
+        return byteArrayPointer;
+    }
+
+    [UnmanagedCallersOnly(EntryPoint = "DbById")]
+    public static unsafe IntPtr DbById(int id, int* length, IntPtr* handlePointer)
+    {
+        var world = RawDb.LoadSingleQueryRowById(id).GetAwaiter().GetResult();
+
+        var memoryStream = new MemoryStream();
+        using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
+
+        _worldSerializer.Serialize(utf8JsonWriter, world);
+
+        *length = (int)utf8JsonWriter.BytesCommitted; 
+        byte[] byteArray = memoryStream.ToArray();
+
+        GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
+        IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
+        *handlePointer = GCHandle.ToIntPtr(handle);
+
+        return byteArrayPointer;
+    }
+}

+ 1 - 1
frameworks/CSharp/appmpower/src/CachedWorld.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/CachedWorld.cs

@@ -1,4 +1,4 @@
-namespace appMpower
+namespace appMpower.Orm.Objects
 {
 {
    public struct CachedWorld
    public struct CachedWorld
    {
    {

+ 1 - 3
frameworks/CSharp/appmpower/src/Fortune.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/Fortune.cs

@@ -1,6 +1,4 @@
-using System;
-
-namespace appMpower
+namespace appMpower.Orm.Objects
 {
 {
    public readonly struct Fortune : IComparable<Fortune>, IComparable
    public readonly struct Fortune : IComparable<Fortune>, IComparable
    {
    {

+ 1 - 1
frameworks/CSharp/appmpower/src/World.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Objects/World.cs

@@ -1,4 +1,4 @@
-namespace appMpower
+namespace appMpower.Orm.Objects
 {
 {
    public struct World
    public struct World
    {
    {

+ 50 - 131
frameworks/CSharp/appmpower/src/RawDb.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/RawDb.cs

@@ -1,34 +1,18 @@
-using System;
-using System.Collections.Generic;
 using System.Data;
 using System.Data;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Caching.Memory;
-using appMpower.Data; 
+using appMpower.Orm.Data; 
+using appMpower.Orm.Objects;
 using PlatformBenchmarks;
 using PlatformBenchmarks;
 
 
-namespace appMpower
+namespace appMpower.Orm
 {
 {
    public static class RawDb
    public static class RawDb
    {
    {
       private const int MaxBatch = 500;
       private const int MaxBatch = 500;
 
 
-#if ADO      
-      private static ConcurrentRandom _random = new ConcurrentRandom();
-#else
       private static Random _random = new Random();
       private static Random _random = new Random();
-#endif
 
 
       private static string[] _queriesMultipleRows = new string[MaxBatch + 1];
       private static string[] _queriesMultipleRows = new string[MaxBatch + 1];
 
 
-      private static readonly object[] _cacheKeys = Enumerable.Range(0, 10001).Select((i) => new CacheKey(i)).ToArray();
-
-      private static readonly appMpower.Memory.MemoryCache _cache = new appMpower.Memory.MemoryCache(
-            new appMpower.Memory.MemoryCacheOptions()
-            {
-               ExpirationScanFrequency = TimeSpan.FromMinutes(60)
-            });
-
       public static async Task<World> LoadSingleQueryRow()
       public static async Task<World> LoadSingleQueryRow()
       {
       {
          using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
          using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
@@ -38,7 +22,22 @@ namespace appMpower
 
 
          using (dbCommand)
          using (dbCommand)
          {
          {
-            var world = await ReadSingleRow(dbCommand);
+            World world = await ReadSingleRow(dbCommand);
+
+            return world;
+         }
+      }
+
+      public static async Task<World> LoadSingleQueryRowById(int id)
+      {
+         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
+         await pooledConnection.OpenAsync();
+
+         var (dbCommand, _) = CreateReadCommandById(pooledConnection, id);
+
+         using (dbCommand)
+         {
+            World world = await ReadSingleRow(dbCommand);
 
 
             return world;
             return world;
          }
          }
@@ -76,21 +75,17 @@ namespace appMpower
 
 
          using (dbCommand)
          using (dbCommand)
          {
          {
-            var dataReader = await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleResult & CommandBehavior.SequentialAccess);
+            IDataReader dataReader = await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleResult & CommandBehavior.SequentialAccess);
 
 
             while (dataReader.Read())
             while (dataReader.Read())
             {
             {
                fortunes.Add(new Fortune
                fortunes.Add(new Fortune
                (
                (
-                   id: dataReader.GetInt32(0),
-#if MYSQL
-               //MariaDB ODBC connector does not correctly support Japanese characters in combination with default ADO.NET;
-               //as a solution we custom read this string
-                message: ReadColumn(dataReader, 1)
-#else
-                message: dataReader.GetString(1)
-#endif
-            ));
+                  id: dataReader.GetInt32(0),
+                  //MariaDB ODBC connector does not correctly support Japanese characters in combination with default ADO.NET;
+                  //as a solution we custom read this string
+                  message: (Constants.Dbms == Dbms.MySQL ? ReadColumn(dataReader, 1) : dataReader.GetString(1))
+               ));
             }
             }
 
 
             dataReader.Close();
             dataReader.Close();
@@ -106,7 +101,7 @@ namespace appMpower
       {
       {
          var worlds = new World[count];
          var worlds = new World[count];
 
 
-         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
+         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString, true);
          await pooledConnection.OpenAsync();
          await pooledConnection.OpenAsync();
 
 
          var (queryCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
          var (queryCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
@@ -120,14 +115,10 @@ namespace appMpower
             }
             }
          }
          }
 
 
-         using var updateCommand = new DbCommand(PlatformBenchmarks.BatchUpdateString.Query(count), pooledConnection);
+         using var updateCommand = new DbCommand(BatchUpdateString.Query(count), pooledConnection);
 
 
-         var ids = PlatformBenchmarks.BatchUpdateString.Ids;
-         var randoms = PlatformBenchmarks.BatchUpdateString.Randoms;
-
-#if !MYSQL
-         var jds = PlatformBenchmarks.BatchUpdateString.Jds;
-#endif      
+         var ids = BatchUpdateString.Ids;
+         var randoms = BatchUpdateString.Randoms;
 
 
          for (int i = 0; i < count; i++)
          for (int i = 0; i < count; i++)
          {
          {
@@ -139,30 +130,36 @@ namespace appMpower
             worlds[i].RandomNumber = randomNumber;
             worlds[i].RandomNumber = randomNumber;
          }
          }
 
 
-#if !MYSQL
-         for (int i = 0; i < count; i++)
+         if (Constants.Dbms != Dbms.MySQL)
          {
          {
-            updateCommand.CreateParameter(jds[i], DbType.Int32, worlds[i].Id);
+            var jds = BatchUpdateString.Jds;
+
+            for (int i = 0; i < count; i++)
+            {
+               updateCommand.CreateParameter(jds[i], DbType.Int32, worlds[i].Id);
+            }
          }
          }
-#endif
 
 
-         await updateCommand.ExecuteNonQueryAsync();
+         updateCommand.ExecuteNonQuery();
 
 
          return worlds;
          return worlds;
       }
       }
 
 
-      private static (DbCommand dbCommand, IDbDataParameter dbDataParameter) CreateReadCommand(DbConnection pooledConnection)
+      internal static (DbCommand dbCommand, IDbDataParameter dbDataParameter) CreateReadCommand(DbConnection pooledConnection)
       {
       {
-#if ADO         
-         var dbCommand = new DbCommand("SELECT * FROM world WHERE id=@Id", pooledConnection);
-#else         
-         var dbCommand = new DbCommand("SELECT * FROM world WHERE id=?", pooledConnection);
-#endif         
+         DbCommand dbCommand = new DbCommand("SELECT * FROM world WHERE id=?", pooledConnection);
 
 
          return (dbCommand, dbCommand.CreateParameter("Id", DbType.Int32, _random.Next(1, 10001)));
          return (dbCommand, dbCommand.CreateParameter("Id", DbType.Int32, _random.Next(1, 10001)));
       }
       }
 
 
-      private static async Task<World> ReadSingleRow(DbCommand dbCommand)
+      internal static (DbCommand dbCommand, IDbDataParameter dbDataParameter) CreateReadCommandById(DbConnection pooledConnection, int id)
+      {
+         DbCommand dbCommand = new DbCommand("SELECT * FROM world WHERE id=?", pooledConnection);
+
+         return (dbCommand, dbCommand.CreateParameter("Id", DbType.Int32, id));
+      }
+
+      internal static async Task<World> ReadSingleRow(DbCommand dbCommand)
       {
       {
          var dataReader = await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleRow & CommandBehavior.SequentialAccess);
          var dataReader = await dbCommand.ExecuteReaderAsync(CommandBehavior.SingleRow & CommandBehavior.SequentialAccess);
 
 
@@ -182,7 +179,7 @@ namespace appMpower
       public static async Task<World[]> ReadMultipleRows(int count)
       public static async Task<World[]> ReadMultipleRows(int count)
       {
       {
          int j = 0;
          int j = 0;
-         var ids = PlatformBenchmarks.BatchUpdateString.Ids;
+         var ids = BatchUpdateString.Ids;
          var worlds = new World[count];
          var worlds = new World[count];
          string queryString;
          string queryString;
 
 
@@ -192,14 +189,14 @@ namespace appMpower
          }
          }
          else
          else
          {
          {
-            var stringBuilder = PlatformBenchmarks.StringBuilderCache.Acquire();
+            var stringBuilder = StringBuilderCache.Acquire();
 
 
             for (int i = 0; i < count; i++)
             for (int i = 0; i < count; i++)
             {
             {
                stringBuilder.Append("SELECT * FROM world WHERE id=?;");
                stringBuilder.Append("SELECT * FROM world WHERE id=?;");
             }
             }
 
 
-            queryString = _queriesMultipleRows[count] = PlatformBenchmarks.StringBuilderCache.GetStringAndRelease(stringBuilder);
+            queryString = _queriesMultipleRows[count] = StringBuilderCache.GetStringAndRelease(stringBuilder);
          }
          }
 
 
          using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
          using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
@@ -249,83 +246,5 @@ namespace appMpower
 
 
          return System.Text.Encoding.Default.GetString(values);
          return System.Text.Encoding.Default.GetString(values);
       }
       }
-
-      public static async Task PopulateCache()
-      {
-         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
-         await pooledConnection.OpenAsync();
-
-         var (dbCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
-
-         using (dbCommand)
-         {
-            var cacheKeys = _cacheKeys;
-            var cache = _cache;
-
-            for (var i = 1; i < 10001; i++)
-            {
-               dbDataParameter.Value = i;
-               cache.Set<CachedWorld>(cacheKeys[i], await ReadSingleRow(dbCommand));
-            }
-         }
-      }
-
-      public static Task<CachedWorld[]> LoadCachedQueries(int count)
-      {
-         var result = new CachedWorld[count];
-         var cacheKeys = _cacheKeys;
-         var cache = _cache;
-         var random = _random;
-
-         for (var i = 0; i < result.Length; i++)
-         {
-            var id = random.Next(1, 10001);
-            var key = cacheKeys[id];
-
-            if (cache.TryGetValue(key, out object cached))
-            {
-               result[i] = (CachedWorld)cached;
-            }
-            else
-            {
-               //return LoadUncachedQueries(id, i, count, this, result);
-               return LoadUncachedQueries(id, i, count, result);
-            }
-         }
-
-         return Task.FromResult(result);
-      }
-
-      static async Task<CachedWorld[]> LoadUncachedQueries(int id, int i, int count, CachedWorld[] result)
-      {
-         using var pooledConnection = new DbConnection(DbProviderFactory.ConnectionString);
-         await pooledConnection.OpenAsync();
-
-         var (dbCommand, dbDataParameter) = CreateReadCommand(pooledConnection);
-
-         using (dbCommand)
-         {
-            Func<ICacheEntry, Task<CachedWorld>> create = async (entry) =>
-            {
-               return await ReadSingleRow(dbCommand);
-            };
-
-            var cacheKeys = _cacheKeys;
-            var key = cacheKeys[id];
-
-            dbDataParameter.Value = id;
-
-            for (; i < result.Length; i++)
-            {
-               result[i] = await _cache.GetOrCreateAsync<CachedWorld>(key, create);
-
-               id = _random.Next(1, 10001);
-               dbDataParameter.Value = id;
-               key = cacheKeys[id];
-            }
-         }
-
-         return result;
-      }
    }
    }
 }
 }

+ 24 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/FortunesSerializer.cs

@@ -0,0 +1,24 @@
+using System.Text.Json;
+using appMpower.Orm.Objects;
+
+namespace appMpower.Orm.Serializers
+{
+   public class FortunesSerializer : IJsonSerializer<List<Fortune>>
+   {
+      public void Serialize(Utf8JsonWriter utf8JsonWriter, List<Fortune> fortunes)
+      {
+         utf8JsonWriter.WriteStartArray();
+
+         foreach (Fortune fortune in fortunes)
+         {
+            utf8JsonWriter.WriteStartObject();
+            utf8JsonWriter.WriteNumber("id", fortune.Id);
+            utf8JsonWriter.WriteString("message", fortune.Message);
+            utf8JsonWriter.WriteEndObject();
+         }
+
+         utf8JsonWriter.WriteEndArray();
+         utf8JsonWriter.Flush();
+      }
+   }
+}

+ 9 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/IJsonSerializer.cs

@@ -0,0 +1,9 @@
+using System.Text.Json;
+
+namespace appMpower.Orm.Serializers
+{
+   public interface IJsonSerializer<T>
+   {
+      public void Serialize(Utf8JsonWriter utf8JsonWriter, T t);
+   }
+}

+ 4 - 2
frameworks/CSharp/appmpower/src/WorldSerializer.cs → frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldSerializer.cs

@@ -1,8 +1,9 @@
 using System.Text.Json;
 using System.Text.Json;
+using appMpower.Orm.Objects;
 
 
-namespace appMpower
+namespace appMpower.Orm.Serializers
 {
 {
-   public class WorldSerializer : Kestrel.IJsonSerializer<World>
+   public class WorldSerializer : IJsonSerializer<World>
    {
    {
       public void Serialize(Utf8JsonWriter utf8JsonWriter, World world)
       public void Serialize(Utf8JsonWriter utf8JsonWriter, World world)
       {
       {
@@ -10,6 +11,7 @@ namespace appMpower
          utf8JsonWriter.WriteNumber("id", world.Id);
          utf8JsonWriter.WriteNumber("id", world.Id);
          utf8JsonWriter.WriteNumber("randomNumber", world.RandomNumber);
          utf8JsonWriter.WriteNumber("randomNumber", world.RandomNumber);
          utf8JsonWriter.WriteEndObject();
          utf8JsonWriter.WriteEndObject();
+         utf8JsonWriter.Flush();
       }
       }
    }
    }
 }
 }

+ 24 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/Serializers/WorldsSerizalizer.cs

@@ -0,0 +1,24 @@
+using System.Text.Json;
+using appMpower.Orm.Objects;
+
+namespace appMpower.Orm.Serializers
+{
+   public class WorldsSerializer : IJsonSerializer<World[]>
+   {
+      public void Serialize(Utf8JsonWriter utf8JsonWriter, World[] worlds)
+      {
+         utf8JsonWriter.WriteStartArray();
+
+         foreach (World world in worlds)
+         {
+            utf8JsonWriter.WriteStartObject();
+            utf8JsonWriter.WriteNumber("id", world.Id);
+            utf8JsonWriter.WriteNumber("randomNumber", world.RandomNumber);
+            utf8JsonWriter.WriteEndObject();
+         }
+
+         utf8JsonWriter.WriteEndArray();
+         utf8JsonWriter.Flush();
+      }
+   }
+}

+ 42 - 0
frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj

@@ -0,0 +1,42 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net9.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+
+    <PublishAot>true</PublishAot>
+    <SelfContained>true</SelfContained>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    
+    <RuntimeIdentifier>linux-x64</RuntimeIdentifier> <!-- docker server -->
+    <!--TODOLOCAL-->
+    <!--<RuntimeIdentifier>linux-arm64</RuntimeIdentifier>--> <!-- docker local -->
+    <!--<RuntimeIdentifier>osx-arm64</RuntimeIdentifier>--> <!-- outside docker local -->
+
+    <InvariantGlobalization>true</InvariantGlobalization>
+    <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
+    <IlcOptimizationPreference>Size</IlcOptimizationPreference>
+    <DebugType>none</DebugType>
+    <GenerateRuntimeConfigurationFiles>false</GenerateRuntimeConfigurationFiles>
+
+    <IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
+    <IlcTrimMetadata>true</IlcTrimMetadata>
+    <IlcInvariantGlobalization>true</IlcInvariantGlobalization>
+    <IlcGenerateCompleteTypeMetadata>false</IlcGenerateCompleteTypeMetadata>
+
+    <IlcDisableReflection>true</IlcDisableReflection>
+
+    <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
+
+    <IlcPgoOptimize>true</IlcPgoOptimize>
+
+    <UseSystemResourceKeys>true</UseSystemResourceKeys>
+    <EventSourceSupport>false</EventSourceSupport>
+    <DebuggerSupport>false</DebuggerSupport>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="System.Data.Odbc" Version="9.0.0" />
+  </ItemGroup>
+
+</Project>

+ 0 - 55
frameworks/CSharp/appmpower/src/appMpower.csproj

@@ -1,55 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
-
-  <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
-    <OutputType>Exe</OutputType>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-
-    <!--<RuntimeIdentifier>linux-x64</RuntimeIdentifier>-->
-
-    <!-- Normal .NET 8 -->
-    <PublishAot>true</PublishAot>
-    <SelfContained>true</SelfContained>
-    <InvariantGlobalization>true</InvariantGlobalization>
-    <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
-    <IlcOptimizationPreference>Speed</IlcOptimizationPreference>
-    <DebugType>none</DebugType>
-    <GenerateRuntimeConfigurationFiles>false</GenerateRuntimeConfigurationFiles>
-
-    <!-- Only some may work - From the experimental AOT version -->
-    <IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
-    <IlcTrimMetadata>true</IlcTrimMetadata>
-    <IlcInvariantGlobalization>true</IlcInvariantGlobalization>
-    <IlcGenerateCompleteTypeMetadata>false</IlcGenerateCompleteTypeMetadata>
-
-    <!-- Still works from the experimental AOT version, but high risk -->
-    <IlcDisableReflection>true</IlcDisableReflection>
-
-    <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
-
-    <!--
-    <TrimMode>link</TrimMode>
-    -->
-
-    <!-- Opt out of the "easy mode" of the CoreRT compiler (http://aka.ms/OptimizeCoreRT) -->
-    <IlcPgoOptimize>true</IlcPgoOptimize>
-
-    <!-- This benchmark is marked Stripped, so we might as well do this: -->
-    <UseSystemResourceKeys>true</UseSystemResourceKeys>
-    <EventSourceSupport>false</EventSourceSupport>
-    <DebuggerSupport>false</DebuggerSupport>
-
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="System.Data.Odbc" Version="8.0.0" />
-    <PackageReference Include="Npgsql" Version="8.0.2-*" />
-  </ItemGroup>
-
-  <PropertyGroup>
-      <DefineConstants>$(DefineConstants);POSTGRESQL</DefineConstants>
-      <DefineConstants Condition=" '$(Driver)' == 'odbc' ">$(DefineConstants);ODBC</DefineConstants>
-      <DefineConstants Condition=" '$(Driver)' == 'ado' ">$(DefineConstants);ADO</DefineConstants>
-  </PropertyGroup>
-
-</Project>

+ 7 - 0
frameworks/CSharp/appmpower/src/appMpower/JsonMessage.cs

@@ -0,0 +1,7 @@
+namespace appMpower
+{
+  public struct JsonMessage
+  {
+      public string Message { get; set; }
+  }
+}

+ 120 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/CachingMiddelware.cs

@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower;
+
+public class CachingMiddleware
+{
+   private static readonly Dictionary<int, byte[]> _cache = new();
+   private static readonly Random _random = new();
+
+   private static readonly byte[] _startBytes = Encoding.UTF8.GetBytes("[");
+   private static readonly byte[] _endBytes = Encoding.UTF8.GetBytes("]");
+   private static readonly byte[] _comma = Encoding.UTF8.GetBytes(",");
+
+
+   private readonly static KeyValuePair<string, StringValues> _headerServer =
+       new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+   private readonly static KeyValuePair<string, StringValues> _headerContentType =
+        new KeyValuePair<string, StringValues>("Content-Type", new StringValues("application/json"));
+
+   private readonly RequestDelegate _next;
+
+   public CachingMiddleware(RequestDelegate next)
+   {
+      _next = next;
+   }
+
+   public unsafe Task Invoke(HttpContext httpContext)
+   {
+      if (httpContext.Request.Path.StartsWithSegments("/cached-worlds", StringComparison.Ordinal))
+      {
+         int payloadLength;
+         IntPtr handlePointer;
+         IntPtr bytePointer;
+         byte[] json;
+
+         if (_cache.Count == 0)
+         {
+            for (int i = 1; i < 10001; i++)
+            {
+               bytePointer = NativeMethods.DbById(i, out payloadLength, out handlePointer);
+               json = new byte[payloadLength];
+               Marshal.Copy(bytePointer, json, 0, payloadLength);
+               NativeMethods.FreeHandlePointer(handlePointer);
+               _cache.Add(i, json);
+            }
+         }
+
+         var queryString = httpContext.Request.QueryString.ToString();
+         int queries;
+         Int32.TryParse(queryString.Substring(queryString.LastIndexOf("=") + 1), out queries);
+         queries = queries > 500 ? 500 : (queries > 0 ? queries : 1);
+
+         var response = httpContext.Response;
+         response.Headers.Add(_headerServer);
+         response.Headers.Add(_headerContentType);
+
+         int queriesLength = 0;
+         int[] keys = new int[queries];
+
+         for (int i = 0; i < queries; i++)
+         {
+            keys[i] = _random.Next(1, 10001);
+            
+            if (!_cache.TryGetValue(keys[i], out json))
+            {
+               bytePointer = NativeMethods.DbById(keys[i], out payloadLength, out handlePointer);
+               json = new byte[payloadLength];
+               Marshal.Copy(bytePointer, json, 0, payloadLength);
+               NativeMethods.FreeHandlePointer(handlePointer);
+               _cache.Add(keys[i], json);
+            }
+
+            queriesLength += json.Length;
+         }
+
+         byte[] result = new byte[_startBytes.Length + _endBytes.Length + (_comma.Length * queries - 1) + queriesLength];
+         int position = 0;
+
+         Buffer.BlockCopy(_startBytes, 0, result, position, _startBytes.Length);
+         position += _startBytes.Length;
+
+         for (int i = 0; i < queries; i++)
+         {
+            json = _cache[keys[i]]; 
+            Buffer.BlockCopy(json, 0, result, position, json.Length);
+            position += json.Length;
+
+            if (i < queries - 1)
+            {
+               Buffer.BlockCopy(_comma, 0, result, position, _comma.Length);
+               position += _comma.Length;
+            }
+         }
+
+         Buffer.BlockCopy(_endBytes, 0, result, position, _endBytes.Length);
+
+         response.Headers.Add(
+             new KeyValuePair<string, StringValues>("Content-Length", result.Length.ToString()));
+
+         return response.Body.WriteAsync(result, 0, result.Length);
+      }
+
+      return _next(httpContext);
+   }
+}
+
+public static class CachingMiddlewareExtensions
+{
+   public static IApplicationBuilder UseCaching(this IApplicationBuilder builder)
+   {
+      return builder.UseMiddleware<CachingMiddleware>();
+   }
+}

+ 173 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs

@@ -0,0 +1,173 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Unicode;
+using System.Threading.Tasks;
+using appMpower.Objects;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower; 
+
+public class FortunesMiddleware
+{
+    static readonly HtmlEncoder htmlEncoder = CreateHtmlEncoder();
+    static HtmlEncoder CreateHtmlEncoder()
+    {
+        var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
+        settings.AllowCharacter('\u2014'); // allow EM DASH through
+        return HtmlEncoder.Create(settings);
+    }
+
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("text/html; charset=UTF-8"));
+
+    private static readonly byte[] _delimiter = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF };
+
+    private readonly RequestDelegate _next;
+
+    public FortunesMiddleware(RequestDelegate next)
+    {
+        _next = next;
+    }
+
+    public unsafe Task Invoke(HttpContext httpContext)
+    {
+        if (httpContext.Request.Path.StartsWithSegments("/fortunes", StringComparison.Ordinal))
+        {
+            int payloadLength;
+            IntPtr handlePointer; 
+
+            IntPtr bytePointer = NativeMethods.Fortunes(out payloadLength, out handlePointer);
+
+            /*
+            byte[] json = new byte[payloadLength];
+            Marshal.Copy(bytePointer, json, 0, payloadLength);
+            NativeMethods.FreeHandlePointer(handlePointer);
+
+            string s = Encoding.UTF8.GetString(json, 0, json.Length);
+
+            var options = new JsonSerializerOptions
+            {
+                PropertyNameCaseInsensitive = true
+            };
+
+            List<Fortune> fortunes = JsonSerializer.Deserialize<List<Fortune>>(s, options);
+
+            var response = httpContext.Response; 
+            response.Headers.Add(_headerServer);
+
+            var result = Results.Extensions.RazorSlice<Slices.Fortunes, List<Fortune>>(fortunes);
+            result.HtmlEncoder = htmlEncoder;
+
+            return result.ExecuteAsync(httpContext);
+            */
+
+            byte[] byteArray = new byte[payloadLength];
+            Marshal.Copy(bytePointer, byteArray, 0, payloadLength);
+
+            List<Fortune> fortunes = new List<Fortune>();
+
+            // Convert the byte array into segments split by the delimiter
+            int delimiterLength = _delimiter.Length;
+            int start = 0;
+            int index;
+
+            while ((index = FindDelimiterIndex(byteArray, _delimiter, start)) >= 0)
+            {
+                // Use a span over the segment of bytes for the current object
+                var objectDataSpan = new ReadOnlySpan<byte>(byteArray, start, index - start);
+                Fortune fortune = ConvertBytesToObject(objectDataSpan);
+                fortunes.Add(fortune);
+
+                // Move past the delimiter
+                start = index + delimiterLength;
+            }
+
+            NativeMethods.FreeHandlePointer(handlePointer);
+
+            var response = httpContext.Response; 
+            response.Headers.Add(_headerServer);
+
+            var result = Results.Extensions.RazorSlice<Slices.Fortunes, List<Fortune>>(fortunes);
+            result.HtmlEncoder = htmlEncoder;
+
+            return result.ExecuteAsync(httpContext);
+
+            /*
+            var response = httpContext.Response; 
+            response.Headers.Add(_headerServer);
+            response.Headers.Add(_headerContentType);
+
+            int payloadLength;
+            IntPtr handlePointer; 
+
+            IntPtr bytePointer = NativeMethods.Fortunes(out payloadLength, out handlePointer);
+            byte[] json = new byte[payloadLength];
+            Marshal.Copy(bytePointer, json, 0, payloadLength);
+            NativeMethods.FreeHandlePointer(handlePointer);
+
+            response.Headers.Add(
+                new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
+
+            return response.Body.WriteAsync(json, 0, payloadLength);
+            */
+        }
+
+        return _next(httpContext);
+    }
+
+    private static int FindDelimiterIndex(byte[] array, byte[] delimiter, int startIndex)
+    {
+        int endIndex = array.Length - delimiter.Length;
+
+        for (int i = startIndex; i <= endIndex; i++)
+        {
+            bool isMatch = true;
+
+            for (int j = 0; j < delimiter.Length; j++)
+            {
+                if (array[i + j] != delimiter[j])
+                {
+                    isMatch = false;
+                    break;
+                }
+            }
+
+            if (isMatch)
+            {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    private static Fortune ConvertBytesToObject(ReadOnlySpan<byte> data)
+    {
+        int offset = 0;
+
+        // Read Id
+        int id = BitConverter.ToInt32(data.Slice(offset, sizeof(int)));
+        offset += sizeof(int);
+
+        // Read Message (remaining bytes in the span)
+        string message = Encoding.UTF8.GetString(data.Slice(offset));
+
+        return new Fortune(id, message);
+    }
+}
+
+public static class FortunesMiddlewareExtensions
+{
+    public static IApplicationBuilder UseFortunes(this IApplicationBuilder builder)
+    {
+        return builder.UseMiddleware<FortunesMiddleware>();
+    }
+}

+ 64 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/JsonMiddleware.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Text.Json; 
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+using appMpower.Serializers; 
+
+namespace appMpower; 
+
+public class JsonMiddleware
+{
+    private readonly static JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions
+    {
+        Indented = false, 
+        SkipValidation = true
+    };
+
+    private readonly static JsonMessageSerializer _jsonMessageSerializer = new JsonMessageSerializer();
+
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("application/json"));
+         
+    private readonly RequestDelegate _nextStage;
+
+    public JsonMiddleware(RequestDelegate nextStage)
+    {
+        _nextStage = nextStage;
+    }
+
+    public unsafe Task Invoke(HttpContext httpContext)
+    {
+        if (httpContext.Request.Path.StartsWithSegments("/json", StringComparison.Ordinal))
+        {
+            var response = httpContext.Response; 
+            response.Headers.Add(_headerServer);
+            response.Headers.Add(_headerContentType);
+
+            using var utf8JsonWriter = new Utf8JsonWriter(httpContext.Response.Body, _jsonWriterOptions);
+
+            _jsonMessageSerializer.Serialize(utf8JsonWriter, new JsonMessage { Message = "Hello, World!" });
+
+            response.Headers.Add(
+                new KeyValuePair<string, StringValues>("Content-Length", utf8JsonWriter.BytesPending.ToString()));
+
+            utf8JsonWriter.Flush();
+
+            return Task.CompletedTask;
+        }
+
+        return _nextStage(httpContext);
+    }
+}
+
+public static class JsonMiddlewareExtensions
+{
+    public static IApplicationBuilder UseJson(this IApplicationBuilder builder)
+    {
+        return builder.UseMiddleware<JsonMiddleware>();
+    }
+}

+ 62 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleQueriesMiddleware.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower; 
+
+public class MultipleQueriesMiddleware
+{
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("application/json"));
+
+   private readonly RequestDelegate _next;
+
+   public MultipleQueriesMiddleware(RequestDelegate next)
+   {
+      _next = next;
+   }
+
+    public unsafe Task Invoke(HttpContext httpContext)
+   {
+      if (httpContext.Request.Path.StartsWithSegments("/queries", StringComparison.Ordinal))
+      {
+         var queryString = httpContext.Request.QueryString.ToString(); 
+         int queries; 
+         Int32.TryParse(queryString.Substring(queryString.LastIndexOf("=") + 1), out queries); 
+         queries = queries > 500 ? 500 : (queries > 0 ? queries : 1);
+
+         var response = httpContext.Response;
+         response.Headers.Add(_headerServer);
+         response.Headers.Add(_headerContentType);
+
+         int payloadLength;
+         IntPtr handlePointer;
+
+         IntPtr bytePointer = NativeMethods.Query(queries, out payloadLength, out handlePointer);
+         byte[] json = new byte[payloadLength];
+         Marshal.Copy(bytePointer, json, 0, payloadLength);
+         NativeMethods.FreeHandlePointer(handlePointer);
+
+         response.Headers.Add(
+             new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
+
+         return response.Body.WriteAsync(json, 0, payloadLength);
+      }
+
+      return _next(httpContext);
+   }
+}
+
+public static class MultipleQueriesMiddlewareExtensions
+{
+   public static IApplicationBuilder UseMultipleQueries(this IApplicationBuilder builder)
+   {
+      return builder.UseMiddleware<MultipleQueriesMiddleware>();
+   }
+}

+ 62 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/MultipleUpdatesMiddleware.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower; 
+
+public class MultipleUpdatesMiddelware
+{
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("application/json"));
+
+   private readonly RequestDelegate _next;
+
+   public MultipleUpdatesMiddelware(RequestDelegate next)
+   {
+      _next = next;
+   }
+
+    public unsafe Task Invoke(HttpContext httpContext)
+   {
+      if (httpContext.Request.Path.StartsWithSegments("/updates", StringComparison.Ordinal))
+      {
+         var queryString = httpContext.Request.QueryString.ToString(); 
+         int count; 
+         Int32.TryParse(queryString.Substring(queryString.LastIndexOf("=") + 1), out count); 
+         count = count > 500 ? 500 : (count > 0 ? count : 1);
+
+         var response = httpContext.Response;
+         response.Headers.Add(_headerServer);
+         response.Headers.Add(_headerContentType);
+
+         int payloadLength;
+         IntPtr handlePointer;
+
+         IntPtr bytePointer = NativeMethods.Updates(count, out payloadLength, out handlePointer);
+         byte[] json = new byte[payloadLength];
+         Marshal.Copy(bytePointer, json, 0, payloadLength);
+         NativeMethods.FreeHandlePointer(handlePointer);
+
+         response.Headers.Add(
+             new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
+
+         return response.Body.WriteAsync(json, 0, payloadLength);
+      }
+
+      return _next(httpContext);
+   }
+}
+
+public static class MultipleUpdatesMiddelwareExtensions
+{
+   public static IApplicationBuilder UseMultipleUpdates(this IApplicationBuilder builder)
+   {
+      return builder.UseMiddleware<MultipleUpdatesMiddelware>();
+   }
+}

+ 50 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/PlaintextMiddleware.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower; 
+
+public unsafe class PlaintextMiddleware
+{
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("text/plain"));
+    private static readonly byte[] _helloWorldPayload = Encoding.UTF8.GetBytes("Hello, World!");
+
+    private readonly RequestDelegate _nextStage;
+
+    public PlaintextMiddleware(RequestDelegate nextStage)
+    {
+        _nextStage = nextStage;
+    }
+
+    public Task Invoke(HttpContext httpContext)
+    {
+        if (httpContext.Request.Path.StartsWithSegments("/plaintext", StringComparison.Ordinal))
+        {
+            var payloadLength = _helloWorldPayload.Length;
+            var response = httpContext.Response; 
+            response.Headers.Add(_headerServer);
+            response.Headers.Add(_headerContentType);
+            response.Headers.Add(
+                new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
+
+            return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength);
+        }
+
+        return _nextStage(httpContext);
+    }
+}
+
+public static class PlaintextMiddlewareExtensions
+{
+    public static IApplicationBuilder UsePlainText(this IApplicationBuilder builder)
+    {
+        return builder.UseMiddleware<PlaintextMiddleware>();
+    }
+}

+ 57 - 0
frameworks/CSharp/appmpower/src/appMpower/Middleware/SingleQueryMiddleware.cs

@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace appMpower; 
+
+public class SingleQueryMiddleware
+{
+    private readonly static KeyValuePair<string, StringValues> _headerServer =
+         new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
+    private readonly static KeyValuePair<string, StringValues> _headerContentType =
+         new KeyValuePair<string, StringValues>("Content-Type", new StringValues("application/json"));
+
+    private readonly RequestDelegate _nextStage;
+
+    public SingleQueryMiddleware(RequestDelegate nextStage)
+    {
+        _nextStage = nextStage;
+    }
+
+    public unsafe Task Invoke(HttpContext httpContext)
+    {
+        if (httpContext.Request.Path.StartsWithSegments("/db", StringComparison.Ordinal))
+        {
+            var response = httpContext.Response; 
+            response.Headers.Add(_headerServer);
+            response.Headers.Add(_headerContentType);
+
+            int payloadLength;
+            IntPtr handlePointer; 
+
+            IntPtr bytePointer = NativeMethods.Db(out payloadLength, out handlePointer);
+            byte[] json = new byte[payloadLength];
+            Marshal.Copy(bytePointer, json, 0, payloadLength);
+            NativeMethods.FreeHandlePointer(handlePointer);
+
+            response.Headers.Add(
+                new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
+
+            return response.Body.WriteAsync(json, 0, payloadLength);
+        }
+
+        return _nextStage(httpContext);
+    }
+}
+
+public static class SingleQueryMiddlewareExtensions
+{
+    public static IApplicationBuilder UseSingleQuery(this IApplicationBuilder builder)
+    {
+        return builder.UseMiddleware<SingleQueryMiddleware>();
+    }
+}

+ 62 - 0
frameworks/CSharp/appmpower/src/appMpower/NativeMethods.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Runtime.InteropServices;
+
+public unsafe partial class NativeMethods
+{
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern void Dbms(int dbms); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern void DbProvider(int dbProvider); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl)]
+#endif
+    public static extern void FreeHandlePointer(IntPtr handlePointer);
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    //public static extern byte* Db(out int length); 
+    public static extern IntPtr Db(out int length, out IntPtr handlePointer); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern IntPtr Fortunes(out int length, out IntPtr handlePointer); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern IntPtr Query(int queries, out int length, out IntPtr handlePointer); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern IntPtr Updates(int queries, out int length, out IntPtr handlePointer); 
+
+#if DEBUG
+    [DllImport("appMpower.Orm.dylib", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#else
+    [DllImport("appMpower.Orm.so", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
+#endif   
+    public static extern IntPtr DbById(int id, out int length, out IntPtr handlePointer); 
+}

+ 22 - 0
frameworks/CSharp/appmpower/src/appMpower/Objects/Fortune.cs

@@ -0,0 +1,22 @@
+using System;
+
+namespace appMpower.Objects
+{
+   public struct Fortune : IComparable<Fortune>, IComparable
+   {
+      public Fortune(int id, string message)
+      {
+         Id = id;
+         Message = message;
+      }
+
+      public int Id { get; set; }
+
+      public string Message { get; set; }
+
+      public int CompareTo(object obj) => throw new InvalidOperationException("The non-generic CompareTo should not be used");
+
+      // Performance critical, using culture insensitive comparison
+      public int CompareTo(Fortune other) => string.CompareOrdinal(Message, other.Message);
+   }
+}

+ 44 - 0
frameworks/CSharp/appmpower/src/appMpower/Program.cs

@@ -0,0 +1,44 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+
+namespace appMpower; 
+
+class Program
+{
+    static void Main(string[] args)
+    {
+        BuildWebHost(args).Run();
+    }
+
+    static IHost BuildWebHost(string[] args)
+    {
+        var config = new ConfigurationBuilder()
+            .AddJsonFile("appsettings.json")
+            .AddEnvironmentVariables(prefix: "ASPNETCORE_")
+            .AddCommandLine(args)
+            .Build();
+
+        var appSettings = config.GetSection("AppSettings").Get<AppSettings>();
+
+        var host = Host.CreateDefaultBuilder(args)
+            .ConfigureWebHostDefaults(webBuilder =>
+            {
+                webBuilder.UseConfiguration(config)
+                          .UseKestrel(options =>
+                          {
+                            options.AddServerHeader = false; 
+                            options.AllowSynchronousIO = true;
+                          })
+                          .UseStartup<Startup>();
+            })
+            .Build();
+
+        return host;
+    }
+}
+
+public class AppSettings
+{
+    public string Database { get; set; }
+}

+ 1 - 1
frameworks/CSharp/appmpower/src/Kestrel/IJsonSerializer.cs → frameworks/CSharp/appmpower/src/appMpower/Serializers/IJsonSerializer.cs

@@ -1,6 +1,6 @@
 using System.Text.Json;
 using System.Text.Json;
 
 
-namespace appMpower.Kestrel
+namespace appMpower.Serializers
 {
 {
    public interface IJsonSerializer<T>
    public interface IJsonSerializer<T>
    {
    {

+ 3 - 3
frameworks/CSharp/appmpower/src/JsonMessageSerializer.cs → frameworks/CSharp/appmpower/src/appMpower/Serializers/JsonMessageSerializer.cs

@@ -1,13 +1,13 @@
 using System.Text.Json;
 using System.Text.Json;
 
 
-namespace appMpower
+namespace appMpower.Serializers
 {
 {
-   public class JsonMessageSerializer : Kestrel.IJsonSerializer<JsonMessage>
+   public class JsonMessageSerializer : IJsonSerializer<JsonMessage>
    {
    {
       public void Serialize(Utf8JsonWriter utf8JsonWriter, JsonMessage jsonMessage)
       public void Serialize(Utf8JsonWriter utf8JsonWriter, JsonMessage jsonMessage)
       {
       {
          utf8JsonWriter.WriteStartObject();
          utf8JsonWriter.WriteStartObject();
-         utf8JsonWriter.WriteString("message", jsonMessage.message);
+         utf8JsonWriter.WriteString("message", jsonMessage.Message);
          utf8JsonWriter.WriteEndObject();
          utf8JsonWriter.WriteEndObject();
       }
       }
    }
    }

+ 2 - 0
frameworks/CSharp/appmpower/src/appMpower/Slices/Fortunes.cshtml

@@ -0,0 +1,2 @@
+@inherits RazorSliceHttpResult<List<appMpower.Objects.Fortune>>
+<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>@foreach (var item in Model){<tr><td>@WriteNumber(item.Id, default, CultureInfo.InvariantCulture, false)</td><td>@item.Message</td></tr>}</table></body></html>

+ 10 - 0
frameworks/CSharp/appmpower/src/appMpower/Slices/_ViewImports.cshtml

@@ -0,0 +1,10 @@
+@inherits RazorSliceHttpResult
+
+@using System.Globalization;
+@using Microsoft.AspNetCore.Razor;
+@using Microsoft.AspNetCore.Http.HttpResults;
+@using RazorSlices;
+@using appMpower.Objects;
+
+@tagHelperPrefix __disable_tagHelpers__:
+@removeTagHelper *, Microsoft.AspNetCore.Mvc.Razor

+ 56 - 0
frameworks/CSharp/appmpower/src/appMpower/Startup.cs

@@ -0,0 +1,56 @@
+using System.Text.Encodings.Web;
+using System.Text.Unicode;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace appMpower; 
+
+public class Startup
+{
+    private readonly IConfiguration _configuration;
+
+    public Startup(IConfiguration configuration)
+    {
+        _configuration = configuration;
+    }
+
+    public void ConfigureServices(IServiceCollection services)
+    {
+        var appSettings = _configuration.Get<AppSettings>();
+        services.AddSingleton(appSettings);
+
+#if !DEBUG
+    #if ODBC      
+        NativeMethods.DbProvider(1); //ODBC
+    #else
+        NativeMethods.DbProvider(0); //ADO
+    #endif        
+
+    #if POSTGRESQL      
+        NativeMethods.Dbms(1); //PostgreSQL
+    #else
+        NativeMethods.Dbms(0); //MySQL
+    #endif
+#endif
+
+        var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
+
+        settings.AllowCharacter('—');
+        services.AddWebEncoders(options =>
+        {
+            options.TextEncoderSettings = settings;
+        });
+    }
+
+    public void Configure(IApplicationBuilder app)
+    {
+        app.UsePlainText();
+        app.UseJson();
+        app.UseSingleQuery();
+        app.UseCaching();
+        app.UseFortunes();
+        app.UseMultipleQueries();
+        app.UseMultipleUpdates();
+    }
+}

+ 36 - 0
frameworks/CSharp/appmpower/src/appMpower/appMpower.csproj

@@ -0,0 +1,36 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+  <PropertyGroup>
+    <TargetFramework>net9.0</TargetFramework>
+    <OutputType>Exe</OutputType>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <None Include="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
+
+    <!--
+    <Content Include="../appMpower.Orm/bin/Release/net8.0/appMpower.Orm.dll">
+        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content> 
+    -->
+    <!--TODOLOCAL-->
+    <!--
+    <Content Include="../appMpower.Orm/bin/Release/net8.0/osx-arm64/native/appMpower.Orm.dylib">
+        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content> 
+    -->
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include = "..\appMpower.Orm\appMpower.Orm.csproj" />
+    <PackageReference Include="RazorSlices" Version="0.8.1" />
+  </ItemGroup>
+
+  <PropertyGroup>
+      <DefineConstants Condition=" '$(Driver)' == 'odbc' ">$(DefineConstants);ODBC</DefineConstants>
+      <DefineConstants Condition=" '$(Database)' == 'postgresql' ">$(DefineConstants);POSTGRESQL</DefineConstants>
+      <DefineConstants Condition=" '$(Database)' == 'mysql' ">$(DefineConstants);MYSQL</DefineConstants>
+  </PropertyGroup>
+
+</Project>

+ 9 - 0
frameworks/CSharp/appmpower/src/appMpower/appsettings.json

@@ -0,0 +1,9 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "None",
+      "Microsoft": "None",
+      "Microsoft.Hosting.Lifetime": "None"
+    }
+  }
+}

+ 0 - 0
frameworks/CSharp/appmpower/src/nuget.config → frameworks/CSharp/appmpower/src/appMpower/nuget.config


Some files were not shown because too many files changed in this diff