Skip to content

Commit 40ce60d

Browse files
authored
Merge pull request #38 from TechEmpower/master
aa
2 parents 6a839e8 + 8ee3266 commit 40ce60d

File tree

188 files changed

+2621
-1412
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

188 files changed

+2621
-1412
lines changed

frameworks/CSharp/appmpower/appmpower-odbc-my.dockerfile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build
1+
FROM mcr.microsoft.com/dotnet/sdk:9.0.100 AS build
22
RUN apt-get update
33
RUN apt-get -yqq install clang zlib1g-dev
44
RUN apt-get update
@@ -8,12 +8,12 @@ COPY src .
88
RUN dotnet publish -c Release -o out /p:Database=mysql
99

1010
# Construct the actual image that will run
11-
FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime
11+
FROM mcr.microsoft.com/dotnet/aspnet:9.0.0 AS runtime
1212

1313
RUN apt-get update
1414
# The following installs standard versions unixodbc and pgsqlodbc
1515
# unixodbc still needs to be installed even if compiled locally
16-
RUN apt-get install -y unixodbc wget curl
16+
RUN apt-get install -y unixodbc-dev unixodbc wget curl
1717
RUN apt-get update
1818

1919
WORKDIR /odbc
@@ -45,6 +45,8 @@ WORKDIR /app
4545
COPY --from=build /app/out ./
4646

4747
RUN cp /usr/lib/libm* /app
48+
#RUN cp /usr/lib/aarch64-linux-gnu/libodbc* /app
49+
RUN cp /usr/lib/x86_64-linux-gnu/libodbc* /app
4850

4951
EXPOSE 8080
5052

frameworks/CSharp/appmpower/appmpower-odbc-pg.dockerfile

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build
1+
FROM mcr.microsoft.com/dotnet/sdk:9.0.100 AS build
22
RUN apt-get update
33
RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
44

@@ -7,10 +7,10 @@ COPY src .
77
RUN dotnet publish -c Release -o out /p:Database=postgresql
88

99
# Construct the actual image that will run
10-
FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime
10+
FROM mcr.microsoft.com/dotnet/aspnet:9.0.0 AS runtime
1111

1212
RUN apt-get update
13-
RUN apt-get install -y unixodbc odbc-postgresql
13+
RUN apt-get install -y unixodbc-dev unixodbc odbc-postgresql
1414
# unixodbc still needs to be installed even if compiled locally
1515

1616
ENV PATH=/usr/local/unixODBC/bin:$PATH
@@ -27,6 +27,10 @@ ENV ASPNETCORE_URLS http://+:8080
2727
WORKDIR /app
2828
COPY --from=build /app/out ./
2929

30+
#RUN cp /usr/lib/aarch64-linux-gnu/libodbc* /app
31+
RUN cp /usr/lib/x86_64-linux-gnu/libodbc* /app
32+
33+
3034
EXPOSE 8080
3135

3236
ENTRYPOINT ["./appMpower"]

frameworks/CSharp/appmpower/appmpower.dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/dotnet/sdk:8.0.100 AS build
1+
FROM mcr.microsoft.com/dotnet/sdk:9.0.100 AS build
22
RUN apt-get update
33
RUN apt-get -yqq install clang zlib1g-dev libkrb5-dev libtinfo5
44

@@ -8,7 +8,7 @@ COPY src .
88
RUN dotnet publish -c Release -o out
99

1010
# Construct the actual image that will run
11-
FROM mcr.microsoft.com/dotnet/aspnet:8.0.0 AS runtime
11+
FROM mcr.microsoft.com/dotnet/aspnet:9.0.0 AS runtime
1212
# Full PGO
1313
ENV DOTNET_TieredPGO 1
1414
ENV DOTNET_TC_QuickJitForLoops 1

frameworks/CSharp/appmpower/src/appMpower.Orm/NativeMethods.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ public static class NativeMethods
1717

1818
private readonly static WorldSerializer _worldSerializer = new WorldSerializer();
1919
private readonly static WorldsSerializer _worldsSerializer = new WorldsSerializer();
20+
private readonly static FortunesSerializer _fortunesSerializer = new FortunesSerializer();
21+
private static readonly byte[] _delimiter = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF };
22+
2023

2124
[UnmanagedCallersOnly(EntryPoint = "Dbms")]
2225
public static void Dbms(int dbms)
@@ -66,6 +69,7 @@ public static unsafe IntPtr Db(int* length, IntPtr* handlePointer)
6669
*/
6770
}
6871

72+
/*
6973
[UnmanagedCallersOnly(EntryPoint = "Fortunes")]
7074
public static unsafe IntPtr Fortunes(int* length, IntPtr* handlePointer)
7175
{
@@ -81,6 +85,61 @@ public static unsafe IntPtr Fortunes(int* length, IntPtr* handlePointer)
8185
8286
return byteArrayPointer;
8387
}
88+
*/
89+
90+
[UnmanagedCallersOnly(EntryPoint = "Fortunes")]
91+
public static unsafe IntPtr Fortunes(int* length, IntPtr* handlePointer)
92+
{
93+
List<Fortune> fortunes = RawDb.LoadFortunesRows().GetAwaiter().GetResult();
94+
95+
int totalSize = 0;
96+
97+
foreach (var fortune in fortunes)
98+
{
99+
totalSize += sizeof(int) // for Id
100+
+ Encoding.UTF8.GetByteCount(fortune.Message ?? "") // for Message
101+
+ _delimiter.Length; // for delimiter
102+
}
103+
104+
// Allocate the total buffer
105+
byte[] buffer = new byte[totalSize];
106+
int offset = 0;
107+
108+
// Write each object to the buffer
109+
foreach (var fortune in fortunes)
110+
{
111+
// Write Id
112+
BitConverter.TryWriteBytes(buffer.AsSpan(offset, sizeof(int)), fortune.Id);
113+
offset += sizeof(int);
114+
115+
// Write Message
116+
int descriptionLength = Encoding.UTF8.GetBytes(fortune.Message ?? "", buffer.AsSpan(offset));
117+
offset += descriptionLength;
118+
119+
// Write Delimiter
120+
_delimiter.CopyTo(buffer, offset);
121+
offset += _delimiter.Length;
122+
}
123+
124+
byte[] byteArray = buffer.ToArray();
125+
*length = byteArray.Length;
126+
127+
/*
128+
var memoryStream = new MemoryStream();
129+
using var utf8JsonWriter = new Utf8JsonWriter(memoryStream, _jsonWriterOptions);
130+
131+
_fortunesSerializer.Serialize(utf8JsonWriter, fortunes);
132+
133+
byte[] byteArray = memoryStream.ToArray();
134+
*length = (int)utf8JsonWriter.BytesCommitted;
135+
*/
136+
137+
GCHandle handle = GCHandle.Alloc(byteArray, GCHandleType.Pinned);
138+
IntPtr byteArrayPointer = handle.AddrOfPinnedObject();
139+
*handlePointer = GCHandle.ToIntPtr(handle);
140+
141+
return byteArrayPointer;
142+
}
84143

85144
[UnmanagedCallersOnly(EntryPoint = "Query")]
86145
public static unsafe IntPtr Query(int queries, int* length, IntPtr* handlePointer)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Text.Json;
2+
using appMpower.Orm.Objects;
3+
4+
namespace appMpower.Orm.Serializers
5+
{
6+
public class FortunesSerializer : IJsonSerializer<List<Fortune>>
7+
{
8+
public void Serialize(Utf8JsonWriter utf8JsonWriter, List<Fortune> fortunes)
9+
{
10+
utf8JsonWriter.WriteStartArray();
11+
12+
foreach (Fortune fortune in fortunes)
13+
{
14+
utf8JsonWriter.WriteStartObject();
15+
utf8JsonWriter.WriteNumber("id", fortune.Id);
16+
utf8JsonWriter.WriteString("message", fortune.Message);
17+
utf8JsonWriter.WriteEndObject();
18+
}
19+
20+
utf8JsonWriter.WriteEndArray();
21+
utf8JsonWriter.Flush();
22+
}
23+
}
24+
}

frameworks/CSharp/appmpower/src/appMpower.Orm/appMpower.Orm.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
4+
<TargetFramework>net9.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66

77
<PublishAot>true</PublishAot>
@@ -36,7 +36,7 @@
3636
</PropertyGroup>
3737

3838
<ItemGroup>
39-
<PackageReference Include="System.Data.Odbc" Version="8.0.0" />
39+
<PackageReference Include="System.Data.Odbc" Version="9.0.0" />
4040
</ItemGroup>
4141

4242
</Project>

frameworks/CSharp/appmpower/src/appMpower/Middleware/FortunesMiddleware.cs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
using System.Collections.Generic;
33
using System.Runtime.InteropServices;
44
using System.Text;
5+
using System.Text.Encodings.Web;
6+
using System.Text.Json;
7+
using System.Text.Unicode;
58
using System.Threading.Tasks;
9+
using appMpower.Objects;
610
using Microsoft.AspNetCore.Builder;
711
using Microsoft.AspNetCore.Http;
812
using Microsoft.Extensions.Primitives;
@@ -11,11 +15,21 @@ namespace appMpower;
1115

1216
public class FortunesMiddleware
1317
{
18+
static readonly HtmlEncoder htmlEncoder = CreateHtmlEncoder();
19+
static HtmlEncoder CreateHtmlEncoder()
20+
{
21+
var settings = new TextEncoderSettings(UnicodeRanges.BasicLatin, UnicodeRanges.Katakana, UnicodeRanges.Hiragana);
22+
settings.AllowCharacter('\u2014'); // allow EM DASH through
23+
return HtmlEncoder.Create(settings);
24+
}
25+
1426
private readonly static KeyValuePair<string, StringValues> _headerServer =
1527
new KeyValuePair<string, StringValues>("Server", new StringValues("k"));
1628
private readonly static KeyValuePair<string, StringValues> _headerContentType =
1729
new KeyValuePair<string, StringValues>("Content-Type", new StringValues("text/html; charset=UTF-8"));
1830

31+
private static readonly byte[] _delimiter = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF };
32+
1933
private readonly RequestDelegate _next;
2034

2135
public FortunesMiddleware(RequestDelegate next)
@@ -27,6 +41,66 @@ public unsafe Task Invoke(HttpContext httpContext)
2741
{
2842
if (httpContext.Request.Path.StartsWithSegments("/fortunes", StringComparison.Ordinal))
2943
{
44+
int payloadLength;
45+
IntPtr handlePointer;
46+
47+
IntPtr bytePointer = NativeMethods.Fortunes(out payloadLength, out handlePointer);
48+
49+
/*
50+
byte[] json = new byte[payloadLength];
51+
Marshal.Copy(bytePointer, json, 0, payloadLength);
52+
NativeMethods.FreeHandlePointer(handlePointer);
53+
54+
string s = Encoding.UTF8.GetString(json, 0, json.Length);
55+
56+
var options = new JsonSerializerOptions
57+
{
58+
PropertyNameCaseInsensitive = true
59+
};
60+
61+
List<Fortune> fortunes = JsonSerializer.Deserialize<List<Fortune>>(s, options);
62+
63+
var response = httpContext.Response;
64+
response.Headers.Add(_headerServer);
65+
66+
var result = Results.Extensions.RazorSlice<Slices.Fortunes, List<Fortune>>(fortunes);
67+
result.HtmlEncoder = htmlEncoder;
68+
69+
return result.ExecuteAsync(httpContext);
70+
*/
71+
72+
byte[] byteArray = new byte[payloadLength];
73+
Marshal.Copy(bytePointer, byteArray, 0, payloadLength);
74+
75+
List<Fortune> fortunes = new List<Fortune>();
76+
77+
// Convert the byte array into segments split by the delimiter
78+
int delimiterLength = _delimiter.Length;
79+
int start = 0;
80+
int index;
81+
82+
while ((index = FindDelimiterIndex(byteArray, _delimiter, start)) >= 0)
83+
{
84+
// Use a span over the segment of bytes for the current object
85+
var objectDataSpan = new ReadOnlySpan<byte>(byteArray, start, index - start);
86+
Fortune fortune = ConvertBytesToObject(objectDataSpan);
87+
fortunes.Add(fortune);
88+
89+
// Move past the delimiter
90+
start = index + delimiterLength;
91+
}
92+
93+
NativeMethods.FreeHandlePointer(handlePointer);
94+
95+
var response = httpContext.Response;
96+
response.Headers.Add(_headerServer);
97+
98+
var result = Results.Extensions.RazorSlice<Slices.Fortunes, List<Fortune>>(fortunes);
99+
result.HtmlEncoder = htmlEncoder;
100+
101+
return result.ExecuteAsync(httpContext);
102+
103+
/*
30104
var response = httpContext.Response;
31105
response.Headers.Add(_headerServer);
32106
response.Headers.Add(_headerContentType);
@@ -43,10 +117,51 @@ public unsafe Task Invoke(HttpContext httpContext)
43117
new KeyValuePair<string, StringValues>("Content-Length", payloadLength.ToString()));
44118
45119
return response.Body.WriteAsync(json, 0, payloadLength);
120+
*/
46121
}
47122

48123
return _next(httpContext);
49124
}
125+
126+
private static int FindDelimiterIndex(byte[] array, byte[] delimiter, int startIndex)
127+
{
128+
int endIndex = array.Length - delimiter.Length;
129+
130+
for (int i = startIndex; i <= endIndex; i++)
131+
{
132+
bool isMatch = true;
133+
134+
for (int j = 0; j < delimiter.Length; j++)
135+
{
136+
if (array[i + j] != delimiter[j])
137+
{
138+
isMatch = false;
139+
break;
140+
}
141+
}
142+
143+
if (isMatch)
144+
{
145+
return i;
146+
}
147+
}
148+
149+
return -1;
150+
}
151+
152+
private static Fortune ConvertBytesToObject(ReadOnlySpan<byte> data)
153+
{
154+
int offset = 0;
155+
156+
// Read Id
157+
int id = BitConverter.ToInt32(data.Slice(offset, sizeof(int)));
158+
offset += sizeof(int);
159+
160+
// Read Message (remaining bytes in the span)
161+
string message = Encoding.UTF8.GetString(data.Slice(offset));
162+
163+
return new Fortune(id, message);
164+
}
50165
}
51166

52167
public static class FortunesMiddlewareExtensions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
3+
namespace appMpower.Objects
4+
{
5+
public struct Fortune : IComparable<Fortune>, IComparable
6+
{
7+
public Fortune(int id, string message)
8+
{
9+
Id = id;
10+
Message = message;
11+
}
12+
13+
public int Id { get; set; }
14+
15+
public string Message { get; set; }
16+
17+
public int CompareTo(object obj) => throw new InvalidOperationException("The non-generic CompareTo should not be used");
18+
19+
// Performance critical, using culture insensitive comparison
20+
public int CompareTo(Fortune other) => string.CompareOrdinal(Message, other.Message);
21+
}
22+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@inherits RazorSliceHttpResult<List<appMpower.Objects.Fortune>>
2+
<!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>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@inherits RazorSliceHttpResult
2+
3+
@using System.Globalization;
4+
@using Microsoft.AspNetCore.Razor;
5+
@using Microsoft.AspNetCore.Http.HttpResults;
6+
@using RazorSlices;
7+
@using appMpower.Objects;
8+
9+
@tagHelperPrefix __disable_tagHelpers__:
10+
@removeTagHelper *, Microsoft.AspNetCore.Mvc.Razor

0 commit comments

Comments
 (0)