├── README.md
├── arsd-webassembly
├── arsd
│ ├── color.d
│ ├── simpleaudio.d
│ ├── simpledisplay.d
│ └── webassembly.d
├── core
│ ├── arsd
│ │ ├── aa.d
│ │ ├── memory_allocation.d
│ │ ├── objectutils.d
│ │ └── utf_decoding.d
│ └── internal
│ │ └── utf.d
├── object.d
└── std
│ ├── random.d
│ └── stdio.d
├── build.bat
├── fail.d
├── hello.d
├── server
├── Makefile
├── serve.d
├── webassembly-core.js
└── webassembly-skeleton.html
├── test_runtime.d
└── tetris.d
/README.md:
--------------------------------------------------------------------------------
1 | This repo contains my webassembly stuff, intended for use with ldc.
2 |
3 | arsd-webassembly is the library code, including partial source ports
4 | of some libraries I use and a minimal druntime for use on the web.
5 | You should compile with these modules instead of the real libraries.
6 |
7 | server is a little web server and the other bridge code in javascript
8 | and html. Of course you don't need to use my webserver.
9 |
10 | This is EXTREMELY MINIMAL. I only wrote what I needed for my demo. Your
11 | use cases will probably not work.
12 |
--------------------------------------------------------------------------------
/arsd-webassembly/arsd/color.d:
--------------------------------------------------------------------------------
1 | module arsd.color;
2 |
3 | char toHex(int a) {
4 | if(a < 10)
5 | return cast(char) (a + '0');
6 | else
7 | return cast(char) (a - 10 + 'a');
8 | }
9 |
10 | struct Color {
11 | int r, g, b, a;
12 | this(int r, int g, int b, int a = 255) {
13 | this.r = r;
14 | this.g = g;
15 | this.b = b;
16 | this.a = a;
17 | }
18 |
19 | void toTempString(char[] data) {
20 | data[0] = '#';
21 | data[1] = toHex(r >> 4);
22 | data[2] = toHex(r & 0x0f);
23 | data[3] = toHex(g >> 4);
24 | data[4] = toHex(g & 0x0f);
25 | data[5] = toHex(b >> 4);
26 | data[6] = toHex(b & 0x0f);
27 | }
28 |
29 | static Color fromHsl(double h, double s, double l, double a = 255) {
30 | h = h % 360;
31 |
32 | double C = (1 - absInternal(2 * l - 1)) * s;
33 |
34 | double hPrime = h / 60;
35 |
36 | double X = C * (1 - absInternal(hPrime % 2 - 1));
37 |
38 | double r, g, b;
39 |
40 | if(h is double.nan)
41 | r = g = b = 0;
42 | else if (hPrime >= 0 && hPrime < 1) {
43 | r = C;
44 | g = X;
45 | b = 0;
46 | } else if (hPrime >= 1 && hPrime < 2) {
47 | r = X;
48 | g = C;
49 | b = 0;
50 | } else if (hPrime >= 2 && hPrime < 3) {
51 | r = 0;
52 | g = C;
53 | b = X;
54 | } else if (hPrime >= 3 && hPrime < 4) {
55 | r = 0;
56 | g = X;
57 | b = C;
58 | } else if (hPrime >= 4 && hPrime < 5) {
59 | r = X;
60 | g = 0;
61 | b = C;
62 | } else if (hPrime >= 5 && hPrime < 6) {
63 | r = C;
64 | g = 0;
65 | b = X;
66 | }
67 |
68 | double m = l - C / 2;
69 |
70 | r += m;
71 | g += m;
72 | b += m;
73 |
74 | return Color(
75 | cast(int)(r * 255),
76 | cast(int)(g * 255),
77 | cast(int)(b * 255),
78 | cast(int)(a));
79 | }
80 |
81 | static immutable Color white = Color(255, 255, 255, 255);
82 | static immutable Color black = Color(0, 0, 0, 255);
83 | static immutable Color red = Color(255, 0, 0, 255);
84 | static immutable Color blue = Color(0, 0, 255, 255);
85 | static immutable Color green = Color(0, 255, 0, 255);
86 | static immutable Color yellow = Color(255, 255, 0, 255);
87 | static immutable Color teal = Color(0, 255, 255, 255);
88 | static immutable Color purple = Color(255, 0, 255, 255);
89 | static immutable Color gray = Color(127, 127, 127, 255);
90 | static immutable Color transparent = Color(0, 0, 0, 0);
91 | }
92 |
93 | struct Point {
94 | int x;
95 | int y;
96 |
97 | pure const nothrow @safe:
98 |
99 | Point opBinary(string op)(in Point rhs) @nogc {
100 | return Point(mixin("x" ~ op ~ "rhs.x"), mixin("y" ~ op ~ "rhs.y"));
101 | }
102 |
103 | Point opBinary(string op)(int rhs) @nogc {
104 | return Point(mixin("x" ~ op ~ "rhs"), mixin("y" ~ op ~ "rhs"));
105 | }
106 |
107 | }
108 |
109 | struct Size {
110 | int width;
111 | int height;
112 | }
113 |
114 |
115 | nothrow @safe @nogc pure
116 | double absInternal(double a) { return a < 0 ? -a : a; }
117 |
118 | struct Rectangle {
119 | int left; ///
120 | int top; ///
121 | int right; ///
122 | int bottom; ///
123 |
124 | pure const nothrow @safe @nogc:
125 |
126 | ///
127 | this(int left, int top, int right, int bottom) {
128 | this.left = left;
129 | this.top = top;
130 | this.right = right;
131 | this.bottom = bottom;
132 | }
133 |
134 | ///
135 | this(in Point upperLeft, in Point lowerRight) {
136 | this(upperLeft.x, upperLeft.y, lowerRight.x, lowerRight.y);
137 | }
138 |
139 | ///
140 | this(in Point upperLeft, in Size size) {
141 | this(upperLeft.x, upperLeft.y, upperLeft.x + size.width, upperLeft.y + size.height);
142 | }
143 |
144 | ///
145 | @property Point upperLeft() {
146 | return Point(left, top);
147 | }
148 |
149 | ///
150 | @property Point upperRight() {
151 | return Point(right, top);
152 | }
153 |
154 | ///
155 | @property Point lowerLeft() {
156 | return Point(left, bottom);
157 | }
158 |
159 | ///
160 | @property Point lowerRight() {
161 | return Point(right, bottom);
162 | }
163 |
164 | ///
165 | @property Point center() {
166 | return Point((right + left) / 2, (bottom + top) / 2);
167 | }
168 |
169 | ///
170 | @property Size size() {
171 | return Size(width, height);
172 | }
173 |
174 | ///
175 | @property int width() {
176 | return right - left;
177 | }
178 |
179 | ///
180 | @property int height() {
181 | return bottom - top;
182 | }
183 |
184 | /// Returns true if this rectangle entirely contains the other
185 | bool contains(in Rectangle r) {
186 | return contains(r.upperLeft) && contains(r.lowerRight);
187 | }
188 |
189 | /// ditto
190 | bool contains(in Point p) {
191 | return (p.x >= left && p.x < right && p.y >= top && p.y < bottom);
192 | }
193 |
194 | /// Returns true of the two rectangles at any point overlap
195 | bool overlaps(in Rectangle r) {
196 | // the -1 in here are because right and top are exclusive
197 | return !((right-1) < r.left || (r.right-1) < left || (bottom-1) < r.top || (r.bottom-1) < top);
198 | }
199 |
200 | /++
201 | Returns a Rectangle representing the intersection of this and the other given one.
202 |
203 | History:
204 | Added July 1, 2021
205 | +/
206 | Rectangle intersectionOf(in Rectangle r) {
207 | auto tmp = Rectangle(max(left, r.left), max(top, r.top), min(right, r.right), min(bottom, r.bottom));
208 | if(tmp.left >= tmp.right || tmp.top >= tmp.bottom)
209 | tmp = Rectangle.init;
210 |
211 | return tmp;
212 | }
213 | }
214 |
215 | private int max(int a, int b) @nogc nothrow pure @safe {
216 | return a >= b ? a : b;
217 | }
218 | private int min(int a, int b) @nogc nothrow pure @safe {
219 | return a <= b ? a : b;
220 | }
221 |
222 |
223 | enum arsd_jsvar_compatible = "arsd_jsvar_compatible";
224 | class MemoryImage {}
225 |
--------------------------------------------------------------------------------
/arsd-webassembly/arsd/simpleaudio.d:
--------------------------------------------------------------------------------
1 | module arsd.simpleaudio;
2 |
3 | struct AudioOutputThread {
4 | this(int) {}
5 | void start() {}
6 | void beep(int = 0) {}
7 | void boop(int = 0) {}
8 | }
9 |
--------------------------------------------------------------------------------
/arsd-webassembly/arsd/simpledisplay.d:
--------------------------------------------------------------------------------
1 | module arsd.simpledisplay;
2 |
3 | public import arsd.color;
4 |
5 | import arsd.webassembly;
6 |
7 | //shared static this() { eval("hi there"); }
8 |
9 | // the js bridge is SO EXPENSIVE we have to minimize using it.
10 |
11 | class SimpleWindow {
12 | this(int width, int height, string title = "D Application") {
13 | this.width = width;
14 | this.height = height;
15 |
16 | element = eval!NativeHandle(q{
17 | var s = document.getElementById("screen");
18 | var canvas = document.createElement("canvas");
19 | canvas.addEventListener("contextmenu", function(event) { event.preventDefault(); });
20 | canvas.setAttribute("width", $0);
21 | canvas.setAttribute("height", $1);
22 | canvas.setAttribute("title", $2);
23 | s.appendChild(canvas);
24 | return canvas;
25 | }, width, height, title);
26 |
27 | canvasContext = eval!NativeHandle(q{
28 | return $0.getContext("2d");
29 | }, element);
30 | }
31 |
32 | NativeHandle element;
33 | NativeHandle canvasContext;
34 | int width;
35 | int height;
36 |
37 | void close() {
38 | eval(q{ clearInterval($0); }, intervalId);
39 | intervalId = 0;
40 | }
41 |
42 | void delegate() onClosing;
43 |
44 | ScreenPainter draw() {
45 | return ScreenPainter(this);
46 | }
47 |
48 | int intervalId;
49 |
50 | void eventLoop(T...)(int timeout, T t) {
51 | foreach(arg; t) {
52 | static if(is(typeof(arg) : void delegate())) {
53 | sdpy_timer = arg;
54 | } else static if(is(typeof(arg) : void delegate(KeyEvent))) {
55 | sdpy_key = arg;
56 | } else static if(is(typeof(arg) : void delegate(MouseEvent))) {
57 | sdpy_mouse = arg;
58 | } else static assert(0, typeof(arg).stringof);
59 | }
60 |
61 | if(timeout)
62 | intervalId = eval!int(q{
63 | return setInterval(function(a) { exports.sdpy_timer_trigger(); }, $0);
64 | }, timeout);
65 |
66 | eval(q{
67 | function translate(key) {
68 | var k = 0;
69 | switch(key) {
70 | case "[": k = 1; break;
71 | case "]": k = 2; break;
72 | case "Left": case "ArrowLeft": k = 3; break;
73 | case "Right": case "ArrowRight": k = 4; break;
74 | case "Down": case "ArrowDown": k = 5; break;
75 | case "Up": case "ArrowUp": k = 6; break;
76 | case " ": k = 7; break;
77 | // "Enter", "Esc" / "Escape"
78 | default: k = 0;
79 | }
80 | return k;
81 | }
82 | document.body.addEventListener("keydown", function(event) {
83 | exports.sdpy_key_trigger(1, translate(event.key));
84 | event.preventDefault();
85 | }, true);
86 | document.body.addEventListener("keyup", function(event) {
87 | exports.sdpy_key_trigger(0, translate(event.key));
88 | event.preventDefault();
89 | }, true);
90 | $0.addEventListener("mousedown", function(event) {
91 | exports.sdpy_mouse_trigger(1, event.button, event.offsetX, event.offsetY);
92 | }, true);
93 | $0.addEventListener("mouseup", function(event) {
94 | exports.sdpy_mouse_trigger(0, event.button);
95 | }, true);
96 | }, element);
97 |
98 | }
99 | }
100 |
101 | void delegate() sdpy_timer;
102 | void delegate(KeyEvent) sdpy_key;
103 | void delegate(MouseEvent) sdpy_mouse;
104 |
105 | export extern(C) void sdpy_timer_trigger() {
106 | sdpy_timer();
107 | }
108 | export extern(C) void sdpy_key_trigger(int pressed, int key) {
109 | KeyEvent ke;
110 | ke.pressed = pressed ? true : false;
111 | ke.key = key;
112 | if(sdpy_key)
113 | sdpy_key(ke);
114 | }
115 | export extern(C) void sdpy_mouse_trigger(int pressed, int button, int x, int y) {
116 | MouseEvent me;
117 | me.type = pressed ? MouseEventType.buttonPressed : MouseEventType.buttonReleased;
118 | switch(button) {
119 | case 0:
120 | me.button = MouseButton.left;
121 | break;
122 | case 1:
123 | me.button = MouseButton.middle;
124 | break;
125 | case 2:
126 | me.button = MouseButton.right;
127 | break;
128 | default:
129 | }
130 | me.x = x;
131 | me.y = y;
132 | if(sdpy_mouse)
133 | sdpy_mouse(me);
134 |
135 | }
136 |
137 |
138 | // push arguments in reverse order then push the command
139 | enum canvasRender = q{
140 | };
141 |
142 | struct ScreenPainter {
143 | this(SimpleWindow window) {
144 | // no need to arc here tbh
145 | this.w = window.width;
146 | this.h = window.height;
147 |
148 | this.element = NativeHandle(window.element.handle, false);
149 | this.context = NativeHandle(window.canvasContext.handle, false);
150 | }
151 | @disable this(this); // for now...
152 | NativeHandle element;
153 | NativeHandle context;
154 |
155 | private int w, h;
156 |
157 | void clear() {
158 | addCommand(1);
159 | }
160 |
161 | void outlineColor(Color c) {
162 | char[7] data;
163 | c.toTempString(data[]);
164 | addCommand(2, 7, data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
165 | return;
166 | //context.properties.strokeStyle!string = cast(immutable)(data[]);
167 | }
168 | void fillColor(Color c) {
169 | char[7] data;
170 | c.toTempString(data[]);
171 | addCommand(3, 7, data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
172 | return;
173 | //context.properties.fillStyle!string = cast(immutable)(data[]);
174 | }
175 |
176 | void drawPolygon(Point[] points) {
177 | addCommand(8);
178 | addCommand(cast(double) points.length);
179 | foreach(point; points) {
180 | push(cast(double) point.x);
181 | push(cast(double) point.y);
182 | }
183 | }
184 |
185 | void drawRectangle(Point p, int w, int h) {
186 | addCommand(4, p.x, p.y, w, h);
187 | }
188 | void drawRectangle(Point p, Size s) {
189 | drawRectangle(p, s.width, s.height);
190 | }
191 | void drawText(Point p, in char[] txt, Point lowerRight = Point(0, 0), uint alignment = 0) {
192 | // FIXME use the new system
193 | addCommand(5, p.x, p.y + 16, txt.length);
194 | foreach(c; txt)
195 | push(cast(double) c);
196 | return;
197 | eval(q{
198 | var context = $0;
199 | context.font = "18px sans-serif";
200 | context.strokeText($1, $2, $3 + 16);
201 | }, context, txt, p.x, p.y);
202 | }
203 |
204 | void drawCircle(Point upperLeft, int diameter) {
205 | addCommand(6, upperLeft.x + diameter / 2, upperLeft.y + diameter / 2, diameter / 2);
206 | }
207 |
208 | void drawLine(Point p1, Point p2) {
209 | drawLine(p1.x, p1.y, p2.x, p2.y);
210 | }
211 |
212 | void drawLine(int x1, int y1, int x2, int y2) {
213 | addCommand(7, x1, y1, x2, y2);
214 | }
215 |
216 | private:
217 | void addCommand(T...)(int cmd, T args) {
218 | push(cmd);
219 | foreach(arg; args) {
220 | push(arg);
221 | }
222 | }
223 |
224 | // 50ish % on ronaroids total cpu without this
225 | // with it, we at like 16%
226 | static __gshared double[] commandStack;
227 | size_t commandStackPosition;
228 |
229 | void push(T)(T t) {
230 | if(commandStackPosition == commandStack.length) {
231 | commandStack.length = commandStack.length + 1024;
232 | commandStack.assumeUniqueReference();
233 | }
234 |
235 | commandStack[commandStackPosition++] = t;
236 | }
237 |
238 | ~this() {
239 | executeCanvasCommands(this.context.handle, this.commandStack.ptr, commandStackPosition);
240 | }
241 | }
242 |
243 | extern(C) void executeCanvasCommands(int handle, double* start, size_t len);
244 |
245 | struct KeyEvent {
246 | int key;
247 | bool pressed;
248 | }
249 |
250 | enum MouseEventType : int {
251 | motion = 0, /// The mouse moved inside the window
252 | buttonPressed = 1, /// A mouse button was pressed or the wheel was spun
253 | buttonReleased = 2, /// A mouse button was released
254 | }
255 |
256 | struct MouseEvent {
257 | MouseEventType type;
258 | int x;
259 | int y;
260 | int dx;
261 | int dy;
262 |
263 | MouseButton button;
264 | int modifierState;
265 | }
266 |
267 | enum MouseButton : int {
268 | none = 0,
269 | left = 1, ///
270 | right = 2, ///
271 | middle = 4, ///
272 | wheelUp = 8, ///
273 | wheelDown = 16, ///
274 | backButton = 32, /// often found on the thumb and used for back in browsers
275 | forwardButton = 64, /// often found on the thumb and used for forward in browsers
276 | }
277 |
278 | enum TextAlignment : uint {
279 | Left = 0, ///
280 | Center = 1, ///
281 | Right = 2, ///
282 |
283 | VerticalTop = 0, ///
284 | VerticalCenter = 4, ///
285 | VerticalBottom = 8, ///
286 | }
287 |
288 | enum Key {
289 | LeftBracket = 1,
290 | RightBracket,
291 | Left,
292 | Right,
293 | Down,
294 | Up,
295 | Space
296 | }
297 |
298 | enum MouseCursor { cross }
299 |
300 | class OperatingSystemFont {}
301 | enum UsingSimpledisplayX11 = false;
302 | enum SimpledisplayTimerAvailable = false;
303 |
304 |
305 | class Sprite{}
306 |
307 | enum bool OpenGlEnabled = false;
308 |
309 | alias ScreenPainterImplementation = ScreenPainter;
310 |
311 | mixin template ExperimentalTextComponent() {
312 | class TextLayout {
313 |
314 | }
315 | }
316 |
--------------------------------------------------------------------------------
/arsd-webassembly/arsd/webassembly.d:
--------------------------------------------------------------------------------
1 | /+
2 | This is the D interface to my webassembly javascript bridge.
3 | +/
4 | module arsd.webassembly;
5 |
6 | struct AcquireArgument {
7 | int type;
8 | const(void)* ptr;
9 | int length;
10 | }
11 |
12 | // the basic bridge functions defined in webassembly-core.js {
13 |
14 | @trusted @nogc pure nothrow
15 | {
16 | extern(C) void retain(int);
17 | extern(C) void release(int);
18 | extern(C) int acquire(int returnType, string callingModuleName, string code, AcquireArgument[] arguments);
19 | extern(C) void abort();
20 | extern(C) int monotimeNow();
21 | }
22 |
23 |
24 | // }
25 |
26 | export extern(C) int invoke_d_array_delegate(size_t ptr, size_t funcptr, ubyte[] arg) {
27 | void delegate(in ubyte[] arr) dg;
28 |
29 | dg.ptr = cast(void*) ptr;
30 | dg.funcptr = cast(typeof(dg.funcptr)) funcptr;
31 |
32 | dg(arg);
33 | return 0;
34 |
35 | };
36 |
37 | /++
38 | Evaluates the given code in Javascript. The arguments are available in JS as $0, $1, $2, ....
39 | The `this` object in the evaluated code is set to an object representing the D module that
40 | you can store some stuff in across calls without having to hit the global namespace.
41 |
42 | Note that if you want to return a value from javascript, you MUST use the return keyword
43 | in the script string.
44 |
45 | Wrong: `eval!NativeHandle("document");`
46 |
47 | Right: `eval!NativeHandle("return document");`
48 | +/
49 | template eval(T = void) {
50 | T eval(Args...)(string code, Args args, string callingModuleName = __MODULE__) @trusted @nogc pure {
51 | AcquireArgument[Args.length] aa;
52 | foreach(idx, ref arg; args) {
53 | // FIXME: some other type for unsigned....
54 | static if(is(typeof(arg) : const int)) {
55 | aa[idx].type = 0;
56 | aa[idx].ptr = cast(void*) arg;
57 | aa[idx].length = arg.sizeof;
58 | } else static if(is(immutable typeof(arg) == immutable string)) {
59 | aa[idx].type = 1;
60 | aa[idx].ptr = arg.ptr;
61 | aa[idx].length = arg.length;
62 | } else static if(is(immutable typeof(arg) == immutable NativeHandle)) {
63 | aa[idx].type = 2;
64 | aa[idx].ptr = cast(void*) arg.handle;
65 | aa[idx].length = NativeHandle.sizeof;
66 | } else static if(is(typeof(arg) : const float)) {
67 | aa[idx].type = 3;
68 | aa[idx].ptr = cast(void*) &arg;
69 | aa[idx].length = arg.sizeof;
70 | } else static if(is(immutable typeof(arg) == immutable ubyte[])) {
71 | aa[idx].type = 4;
72 | aa[idx].ptr = arg.ptr;
73 | aa[idx].length = arg.length;
74 | /*
75 | } else static if(is(typeof(arg) == delegate)) {
76 | aa[idx].type = 5;
77 | aa[idx].ptr = cast(void*) &arg;
78 | aa[idx].length = arg.sizeof;
79 | */
80 | } else {
81 | static assert(0);
82 | }
83 | }
84 | static if(is(T == void))
85 | acquire(0, callingModuleName, code, aa[]);
86 | else static if(is(T == int))
87 | return acquire(1, callingModuleName, code, aa[]);
88 | else static if(is(T == float))
89 | return *cast(float*) cast(void*) acquire(2, callingModuleName, code, aa[]);
90 | else static if(is(T == NativeHandle))
91 | return NativeHandle(acquire(3, callingModuleName, code, aa[]));
92 | else static if(is(T == string)) {
93 | auto ptr = cast(int*) acquire(7, callingModuleName, code, aa[]);
94 | auto len = *ptr;
95 | ptr++;
96 | return (cast(immutable(char)*) ptr)[0 .. len];
97 | }
98 | else static assert(0);
99 | }
100 | }
101 |
102 | // and do some opDispatch on the native things to call their methods and it should look p cool
103 |
104 | struct NativeHandle {
105 | @trusted @nogc pure:
106 |
107 | int handle;
108 | bool arc;
109 | this(int handle, bool arc = true) {
110 | this.handle = handle;
111 | this.arc = arc;
112 | }
113 |
114 | this(this) {
115 | if(arc) retain(handle);
116 | }
117 |
118 | ~this() {
119 | if(arc) release(handle);
120 | }
121 |
122 | // never store these, they don't affect the refcount
123 | PropertiesHelper properties() {
124 | return PropertiesHelper(handle);
125 | }
126 |
127 | // never store these, they don't affect the refcount
128 | MethodsHelper methods() {
129 | return MethodsHelper(handle);
130 |
131 | }
132 | }
133 |
134 | struct MethodsHelper {
135 | @trusted @nogc pure:
136 | @disable this();
137 | @disable this(this);
138 |
139 | int handle;
140 | private this(int handle) { this.handle = handle; }
141 |
142 | template opDispatch(string name) {
143 | template opDispatch(T = NativeHandle)
144 | {
145 | T opDispatch(Args...)(Args args, string callingModuleName = __MODULE__) @trusted @nogc pure
146 | {
147 | return eval!T(q{
148 | return $0[$1].apply($0, Array.prototype.slice.call(arguments, 2));
149 | }, NativeHandle(this.handle, false), name, args, callingModuleName);
150 | }
151 | }
152 | }
153 |
154 | }
155 | struct PropertiesHelper {
156 | @trusted @nogc pure:
157 | @disable this();
158 | @disable this(this);
159 |
160 | int handle;
161 | private this(int handle) { this.handle = handle; }
162 |
163 | template opDispatch(string name) {
164 | template opDispatch(T = NativeHandle) {
165 | T opDispatch() {
166 | return eval!T(q{
167 | return $0[$1];
168 | }, NativeHandle(this.handle, false), name);
169 | }
170 |
171 | void opDispatch(T value) {
172 | return eval!void(q{
173 | return $0[$1] = $2;
174 | }, NativeHandle(this.handle, false), name, value);
175 | }
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/arsd-webassembly/core/arsd/aa.d:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of associative arrays.
3 | *
4 | * Copyright: Copyright Digital Mars 2000 - 2015.
5 | * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 | * Authors: Martin Nowak
7 | * Source: $(DRUNTIMESRC rt/_aaA.d)
8 | */
9 | module core.arsd.aa;
10 |
11 | /// AA version for debuggers, bump whenever changing the layout
12 | extern (C) immutable int _aaVersion = 1;
13 |
14 | import core.internal.hash;
15 | import core.arsd.memory_allocation;
16 |
17 | uint min(uint a, uint b) { return a < b ? a : b; }
18 | uint max(uint a, uint b) { return a > b ? a : b; }
19 |
20 | // grow threshold
21 | private enum GROW_NUM = 4;
22 | private enum GROW_DEN = 5;
23 | // shrink threshold
24 | private enum SHRINK_NUM = 1;
25 | private enum SHRINK_DEN = 8;
26 | // grow factor
27 | private enum GROW_FAC = 4;
28 | // growing the AA doubles it's size, so the shrink threshold must be
29 | // smaller than half the grow threshold to have a hysteresis
30 | static assert(GROW_FAC * SHRINK_NUM * GROW_DEN < GROW_NUM * SHRINK_DEN);
31 | // initial load factor (for literals), mean of both thresholds
32 | private enum INIT_NUM = (GROW_DEN * SHRINK_NUM + GROW_NUM * SHRINK_DEN) / 2;
33 | private enum INIT_DEN = SHRINK_DEN * GROW_DEN;
34 |
35 | private enum INIT_NUM_BUCKETS = 8;
36 | // magic hash constants to distinguish empty, deleted, and filled buckets
37 | private enum HASH_EMPTY = 0;
38 | private enum HASH_DELETED = 0x1;
39 | private enum HASH_FILLED_MARK = size_t(1) << 8 * size_t.sizeof - 1;
40 |
41 | // The compiler uses `void*` for its prototypes.
42 | // Don't wrap in a struct to maintain ABI compatibility.
43 | alias AA = Impl*;
44 |
45 | private bool empty(scope const AA impl) pure nothrow @nogc
46 | {
47 | return impl is null || !impl.length;
48 | }
49 |
50 | private struct Impl
51 | {
52 | private:
53 | this(scope const TypeInfo_AssociativeArray ti, size_t sz = INIT_NUM_BUCKETS)
54 | {
55 | keysz = cast(uint) ti.key.size;
56 | valsz = cast(uint) ti.value.size;
57 | buckets = allocBuckets(sz);
58 | firstUsed = cast(uint) buckets.length;
59 | valoff = cast(uint) talign(keysz, ti.value.talign);
60 |
61 | import core.arsd.objectutils : hasPostblit;
62 |
63 | if (hasPostblit(cast()ti.key))
64 | flags |= Flags.keyHasPostblit;
65 | if ((ti.key.flags | ti.value.flags) & 1)
66 | flags |= Flags.hasPointers;
67 |
68 | entryTI = fakeEntryTI(this, ti.key, ti.value);
69 | }
70 |
71 | Bucket[] buckets;
72 | uint used;
73 | uint deleted;
74 | TypeInfo_Struct entryTI;
75 | uint firstUsed;
76 | immutable uint keysz;
77 | immutable uint valsz;
78 | immutable uint valoff;
79 | Flags flags;
80 |
81 | enum Flags : ubyte
82 | {
83 | none = 0x0,
84 | keyHasPostblit = 0x1,
85 | hasPointers = 0x2,
86 | }
87 |
88 | @property size_t length() const pure nothrow @nogc
89 | {
90 | assert(used >= deleted);
91 | return used - deleted;
92 | }
93 |
94 | @property size_t dim() const pure nothrow @nogc @safe
95 | {
96 | return buckets.length;
97 | }
98 |
99 | @property size_t mask() const pure nothrow @nogc
100 | {
101 | return dim - 1;
102 | }
103 |
104 | // find the first slot to insert a value with hash
105 | inout(Bucket)* findSlotInsert(size_t hash) inout pure nothrow @nogc
106 | {
107 | for (size_t i = hash & mask, j = 1;; ++j)
108 | {
109 | if (!buckets[i].filled)
110 | return &buckets[i];
111 | i = (i + j) & mask;
112 | }
113 | }
114 |
115 | // lookup a key
116 | inout(Bucket)* findSlotLookup(size_t hash, scope const void* pkey, scope const TypeInfo keyti) inout
117 | {
118 | for (size_t i = hash & mask, j = 1;; ++j)
119 | {
120 | if (buckets[i].hash == hash && keyti.equals(pkey, buckets[i].entry))
121 | return &buckets[i];
122 | else if (buckets[i].empty)
123 | return null;
124 | i = (i + j) & mask;
125 | }
126 | }
127 |
128 | void grow(scope const TypeInfo keyti)
129 | {
130 | // If there are so many deleted entries, that growing would push us
131 | // below the shrink threshold, we just purge deleted entries instead.
132 | if (length * SHRINK_DEN < GROW_FAC * dim * SHRINK_NUM)
133 | resize(dim);
134 | else
135 | resize(GROW_FAC * dim);
136 | }
137 |
138 | void shrink(scope const TypeInfo keyti)
139 | {
140 | if (dim > INIT_NUM_BUCKETS)
141 | resize(dim / GROW_FAC);
142 | }
143 |
144 | void resize(size_t ndim)
145 | {
146 | auto obuckets = buckets;
147 | buckets = allocBuckets(ndim);
148 |
149 | foreach (ref b; obuckets[firstUsed .. $])
150 | if (b.filled)
151 | *findSlotInsert(b.hash) = b;
152 |
153 | firstUsed = 0;
154 | used -= deleted;
155 | deleted = 0;
156 | free(cast(ubyte*)(obuckets.ptr)); // safe to free b/c impossible to reference
157 | }
158 |
159 | void clear() pure nothrow
160 | {
161 | import core.stdc.string : memset;
162 | // clear all data, but don't change bucket array length
163 | memset(&buckets[firstUsed], 0, (buckets.length - firstUsed) * Bucket.sizeof);
164 | deleted = used = 0;
165 | firstUsed = cast(uint) dim;
166 | }
167 | }
168 |
169 | //==============================================================================
170 | // Bucket
171 | //------------------------------------------------------------------------------
172 |
173 | private struct Bucket
174 | {
175 | private pure nothrow @nogc:
176 | size_t hash;
177 | void* entry;
178 |
179 | @property bool empty() const
180 | {
181 | return hash == HASH_EMPTY;
182 | }
183 |
184 | @property bool deleted() const
185 | {
186 | return hash == HASH_DELETED;
187 | }
188 |
189 | @property bool filled() const @safe
190 | {
191 | return cast(ptrdiff_t) hash < 0;
192 | }
193 | }
194 |
195 | Bucket[] allocBuckets(size_t dim) @trusted
196 | {
197 | enum attr = 0b0001_0000; //enum attr = GC.BlkAttr.NO_INTERIOR;
198 | immutable sz = dim * Bucket.sizeof;
199 | return (cast(Bucket*) calloc(sz, attr))[0 .. dim];
200 | }
201 |
202 | //==============================================================================
203 | // Entry
204 | //------------------------------------------------------------------------------
205 |
206 | private void* allocEntry(scope const Impl* aa, scope const void* pkey)
207 | {
208 | immutable akeysz = aa.valoff;
209 | void* res = void;
210 | if(aa.entryTI)
211 | res = _d_newitemU(aa.entryTI);
212 | else
213 | res = malloc(akeysz + aa.valsz).ptr;
214 |
215 | memcpy(res, pkey, aa.keysz); // copy key
216 | memset(res + akeysz, 0, aa.valsz); // zero value
217 |
218 | return res;
219 | }
220 |
221 | package void entryDtor(void* p, const TypeInfo_Struct sti)
222 | {
223 | // key and value type info stored after the TypeInfo_Struct by tiEntry()
224 | auto sizeti = __traits(classInstanceSize, TypeInfo_Struct);
225 | auto extra = cast(const(TypeInfo)*)(cast(void*) sti + sizeti);
226 | extra[0].destroy(p);
227 | extra[1].destroy(p + talign(extra[0].size, extra[1].talign));
228 | }
229 |
230 | private bool hasDtor(const TypeInfo ti) pure nothrow
231 | {
232 |
233 | if (typeid(ti) is typeid(TypeInfo_Struct))
234 | if ((cast(TypeInfo_Struct) cast(void*) ti).xdtor)
235 | return true;
236 | if (typeid(ti) is typeid(TypeInfo_StaticArray))
237 | return hasDtor(cast()ti.next);
238 |
239 | return false;
240 | }
241 |
242 | // build type info for Entry with additional key and value fields
243 | TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti)
244 | {
245 | import core.arsd.objectutils;
246 | //Same as unqualify
247 | auto kti = unqualify(keyti);
248 | auto vti = unqualify(valti);
249 |
250 |
251 | bool entryHasDtor = hasDtor(kti) || hasDtor(vti);
252 | if (!entryHasDtor)
253 | return null;
254 |
255 | // save kti and vti after type info for struct
256 | enum sizeti = __traits(classInstanceSize, TypeInfo_Struct);
257 | void* p = malloc(sizeti + (2) * (void*).sizeof).ptr;
258 |
259 | memcpy(p, __traits(initSymbol, TypeInfo_Struct).ptr, sizeti);
260 |
261 | auto ti = cast(TypeInfo_Struct) p;
262 | auto extra = cast(TypeInfo*)(p + sizeti);
263 | extra[0] = cast() kti;
264 | extra[1] = cast() vti;
265 |
266 | static immutable tiMangledName = "S2rt3aaA__T5EntryZ";
267 | ti.name = tiMangledName;
268 |
269 |
270 | // we don't expect the Entry objects to be used outside of this module, so we have control
271 | // over the non-usage of the callback methods and other entries and can keep these null
272 | // xtoHash, xopEquals, xopCmp, xtoString and xpostblit
273 | immutable entrySize = aa.valoff + aa.valsz;
274 | ti.m_init = (cast(ubyte*) null)[0 .. entrySize]; // init length, but not ptr
275 |
276 | if (entryHasDtor)
277 | {
278 | // xdtor needs to be built from the dtors of key and value for the GC
279 | ti.xdtorti = &entryDtor;
280 | ti.m_flags |= TypeInfo_Struct.StructFlags.isDynamicType;
281 | }
282 |
283 | ti.align_ = cast(uint) max(kti.talign, vti.talign);
284 |
285 | return ti;
286 | }
287 |
288 |
289 | //==============================================================================
290 | // Helper functions
291 | //------------------------------------------------------------------------------
292 |
293 | private size_t talign(size_t tsize, size_t algn) @safe pure nothrow @nogc
294 | {
295 | immutable mask = algn - 1;
296 | assert(!(mask & algn));
297 | return (tsize + mask) & ~mask;
298 | }
299 |
300 | // mix hash to "fix" bad hash functions
301 | private size_t mix(size_t h) @safe pure nothrow @nogc
302 | {
303 | // final mix function of MurmurHash2
304 | enum m = 0x5bd1e995;
305 | h ^= h >> 13;
306 | h *= m;
307 | h ^= h >> 15;
308 | return h;
309 | }
310 |
311 | private size_t calcHash(scope const void* pkey, scope const TypeInfo keyti) nothrow
312 | {
313 | immutable hash = keyti.getHash(pkey);
314 | // highest bit is set to distinguish empty/deleted from filled buckets
315 | return mix(hash) | HASH_FILLED_MARK;
316 | }
317 |
318 | private size_t nextpow2(const size_t n) pure nothrow @nogc
319 | {
320 | import core.bitop : bsr;
321 |
322 | if (!n)
323 | return 1;
324 |
325 | const isPowerOf2 = !((n - 1) & n);
326 | return 1 << (bsr(n) + !isPowerOf2);
327 | }
328 |
329 |
330 | //==============================================================================
331 | // API Implementation
332 | //------------------------------------------------------------------------------
333 |
334 | /** Allocate associative array data.
335 | * Called for `new SomeAA` expression.
336 | * Params:
337 | * ti = TypeInfo for the associative array
338 | * Returns:
339 | * A new associative array.
340 | */
341 | extern (C) Impl* _aaNew(const TypeInfo_AssociativeArray ti)
342 | {
343 | return new Impl(ti);
344 | }
345 |
346 | /// Determine number of entries in associative array.
347 | extern (C) size_t _aaLen(scope const AA aa) pure nothrow @nogc
348 | {
349 | return aa ? aa.length : 0;
350 | }
351 |
352 | /******************************
353 | * Lookup *pkey in aa.
354 | * Called only from implementation of (aa[key]) expressions when value is mutable.
355 | * Params:
356 | * paa = associative array opaque pointer
357 | * ti = TypeInfo for the associative array
358 | * valsz = ignored
359 | * pkey = pointer to the key value
360 | * Returns:
361 | * if key was in the aa, a mutable pointer to the existing value.
362 | * If key was not in the aa, a mutable pointer to newly inserted value which
363 | * is set to all zeros
364 | */
365 | extern (C) void* _aaGetY(scope ubyte** paa, const TypeInfo_AssociativeArray ti,
366 | const size_t valsz, scope const void* pkey)
367 | {
368 | bool found;
369 | return _aaGetX(paa, ti, valsz, pkey, found);
370 | }
371 |
372 | /******************************
373 | * Lookup *pkey in aa.
374 | * Called only from implementation of require
375 | * Params:
376 | * paa = associative array opaque pointer
377 | * ti = TypeInfo for the associative array
378 | * valsz = ignored
379 | * pkey = pointer to the key value
380 | * found = true if the value was found
381 | * Returns:
382 | * if key was in the aa, a mutable pointer to the existing value.
383 | * If key was not in the aa, a mutable pointer to newly inserted value which
384 | * is set to all zeros
385 | */
386 | extern (C) void* _aaGetX(scope ubyte** paa, const TypeInfo_AssociativeArray ti,
387 | const size_t valsz, scope const void* pkey, out bool found)
388 | {
389 |
390 | // lazily alloc implementation
391 | AA aa = *cast(AA*)paa;
392 | if (aa is null)
393 | {
394 | aa = new Impl(ti);
395 | *cast(AA*)paa = aa;
396 | }
397 |
398 | // get hash and bucket for key
399 | immutable hash = calcHash(pkey, ti.key);
400 |
401 | // found a value => return it
402 | if (auto p = aa.findSlotLookup(hash, pkey, ti.key))
403 | {
404 | found = true;
405 | return p.entry + aa.valoff;
406 | }
407 |
408 | auto p = aa.findSlotInsert(hash);
409 | if (p.deleted)
410 | --aa.deleted;
411 | // check load factor and possibly grow
412 | else if (++aa.used * GROW_DEN > aa.dim * GROW_NUM)
413 | {
414 | aa.grow(ti.key);
415 | p = aa.findSlotInsert(hash);
416 | assert(p.empty);
417 | }
418 |
419 | // update search cache and allocate entry
420 | aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
421 | p.hash = hash;
422 | p.entry = allocEntry(aa, pkey);
423 | // postblit for key
424 | if (aa.flags & Impl.Flags.keyHasPostblit)
425 | {
426 | import core.arsd.objectutils;
427 | __doPostblit(p.entry, aa.keysz, unqualify(ti.key));
428 | }
429 | // return pointer to value
430 | return p.entry + aa.valoff;
431 | }
432 |
433 | /******************************
434 | * Lookup *pkey in aa.
435 | * Called only from implementation of (aa[key]) expressions when value is not mutable.
436 | * Params:
437 | * aa = associative array opaque pointer
438 | * keyti = TypeInfo for the key
439 | * valsz = ignored
440 | * pkey = pointer to the key value
441 | * Returns:
442 | * pointer to value if present, null otherwise
443 | */
444 | extern (C) inout(void)* _aaGetRvalueX(inout ubyte** aa, scope const TypeInfo keyti, const size_t valsz,
445 | scope const void* pkey)
446 | {
447 | return _aaInX(aa, keyti, pkey);
448 | }
449 |
450 | /******************************
451 | * Lookup *pkey in aa.
452 | * Called only from implementation of (key in aa) expressions.
453 | * Params:
454 | * aa = associative array opaque pointer
455 | * keyti = TypeInfo for the key
456 | * pkey = pointer to the key value
457 | * Returns:
458 | * pointer to value if present, null otherwise
459 | */
460 | extern (C) inout(void)* _aaInX(inout ubyte** _aa, scope const TypeInfo keyti, scope const void* pkey)
461 | {
462 | import std.stdio;
463 | AA aa = cast(AA)_aa;
464 | if (aa.empty)
465 | return null;
466 |
467 | immutable hash = calcHash(pkey, keyti);
468 | if (auto p = aa.findSlotLookup(hash, pkey, keyti))
469 | return cast(inout)(p.entry + aa.valoff);
470 | return null;
471 | }
472 |
473 | /// Delete entry scope const AA, return true if it was present
474 | extern (C) bool _aaDelX(ubyte* _aa, scope const TypeInfo keyti, scope const void* pkey)
475 | {
476 | AA aa = cast(AA)_aa;
477 | if (aa.empty)
478 | return false;
479 | immutable hash = calcHash(pkey, keyti);
480 | if (auto p = aa.findSlotLookup(hash, pkey, keyti))
481 | {
482 | // clear entry
483 | p.hash = HASH_DELETED;
484 | p.entry = null;
485 |
486 | ++aa.deleted;
487 | // `shrink` reallocates, and allocating from a finalizer leads to
488 | // InvalidMemoryError: https://issues.dlang.org/show_bug.cgi?id=21442
489 | if (aa.length * SHRINK_DEN < aa.dim * SHRINK_NUM) // && !GC.inFinalizer() no GC so never in finalizer
490 | aa.shrink(keyti);
491 |
492 | return true;
493 | }
494 | return false;
495 | }
496 |
497 | /// Remove all elements from AA.
498 | extern (C) void _aaClear(ubyte* _aa) pure nothrow
499 | {
500 | AA aa = cast(AA)_aa;
501 | if (!aa.empty)
502 | {
503 | aa.clear();
504 | }
505 | }
506 |
507 | /// Rehash AA
508 | extern (C) void* _aaRehash(ubyte** _paa, scope const TypeInfo keyti)
509 | {
510 | AA* paa = cast(AA*)_paa;
511 | AA aa = *paa;
512 | if (!aa.empty)
513 | aa.resize(nextpow2(INIT_DEN * aa.length / INIT_NUM));
514 | return aa;
515 | }
516 |
517 | /// Return a GC allocated array of all values
518 | extern (C) inout(void[]) _aaValues(inout ubyte* _aa, const size_t keysz, const size_t valsz,
519 | const TypeInfo tiValueArray)
520 | {
521 | AA aa = cast(AA)_aa;
522 | if (aa.empty)
523 | return null;
524 |
525 | auto res = _d_newarrayU(tiValueArray, aa.length).ptr;
526 | auto pval = res;
527 |
528 | immutable off = aa.valoff;
529 | foreach (b; aa.buckets[aa.firstUsed .. $])
530 | {
531 | if (!b.filled)
532 | continue;
533 | pval[0 .. valsz] = b.entry[off .. valsz + off];
534 | pval += valsz;
535 | }
536 | // postblit is done in object.values
537 | return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements
538 | }
539 |
540 | /// Return a GC allocated array of all keys
541 | extern (C) inout(void[]) _aaKeys(inout ubyte* _aa, const size_t keysz, const TypeInfo tiKeyArray)
542 | {
543 | AA aa = cast(AA)_aa;
544 | if (aa.empty)
545 | return null;
546 |
547 | auto res = _d_newarrayU(tiKeyArray, aa.length).ptr;
548 | auto pkey = res;
549 |
550 | foreach (b; aa.buckets[aa.firstUsed .. $])
551 | {
552 | if (!b.filled)
553 | continue;
554 | pkey[0 .. keysz] = b.entry[0 .. keysz];
555 | pkey += keysz;
556 | }
557 | // postblit is done in object.keys
558 | return (cast(inout(void)*) res)[0 .. aa.length]; // fake length, return number of elements
559 | }
560 |
561 | // opApply callbacks are extern(D)
562 | extern (D) alias dg_t = int delegate(void*);
563 | extern (D) alias dg2_t = int delegate(void*, void*);
564 |
565 | /// foreach opApply over all values
566 | extern (C) int _aaApply(ubyte* _aa, const size_t keysz, dg_t dg)
567 | {
568 | AA aa = cast(AA)_aa;
569 | if (aa.empty)
570 | return 0;
571 |
572 | immutable off = aa.valoff;
573 | foreach (b; aa.buckets)
574 | {
575 | if (!b.filled)
576 | continue;
577 | if (auto res = dg(b.entry + off))
578 | return res;
579 | }
580 | return 0;
581 | }
582 |
583 | /// foreach opApply over all key/value pairs
584 | extern (C) int _aaApply2(ubyte* _aa, const size_t keysz, dg2_t dg)
585 | {
586 | AA aa = cast(AA)_aa;
587 | if (aa.empty)
588 | return 0;
589 |
590 | immutable off = aa.valoff;
591 | foreach (b; aa.buckets)
592 | {
593 | if (!b.filled)
594 | continue;
595 | if (auto res = dg(b.entry, b.entry + off))
596 | return res;
597 | }
598 | return 0;
599 | }
600 |
601 | /** Construct an associative array of type ti from corresponding keys and values.
602 | * Called for an AA literal `[k1:v1, k2:v2]`.
603 | * Params:
604 | * ti = TypeInfo for the associative array
605 | * keys = array of keys
606 | * vals = array of values
607 | * Returns:
608 | * A new associative array opaque pointer, or null if `keys` is empty.
609 | */
610 | extern (C) ubyte* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys,
611 | void[] vals)
612 | {
613 | assert(keys.length == vals.length);
614 |
615 | immutable keysz = ti.key.size;
616 | immutable valsz = ti.value.size;
617 | immutable length = keys.length;
618 |
619 | if (!length)
620 | return null;
621 |
622 | auto aa = new Impl(ti, nextpow2(INIT_DEN * length / INIT_NUM));
623 |
624 | void* pkey = keys.ptr;
625 | void* pval = vals.ptr;
626 | immutable off = aa.valoff;
627 | uint actualLength = 0;
628 | foreach (_; 0 .. length)
629 | {
630 | immutable hash = calcHash(pkey, ti.key);
631 | auto p = aa.findSlotLookup(hash, pkey, ti.key);
632 | if (p is null)
633 | {
634 | p = aa.findSlotInsert(hash);
635 | p.hash = hash;
636 | p.entry = allocEntry(aa, pkey); // move key, no postblit
637 | aa.firstUsed = min(aa.firstUsed, cast(uint)(p - aa.buckets.ptr));
638 | actualLength++;
639 | }
640 | else if (aa.entryTI && hasDtor(ti.value))
641 | {
642 | // destroy existing value before overwriting it
643 | ti.value.destroy(p.entry + off);
644 | }
645 | // set hash and blit value
646 | auto pdst = p.entry + off;
647 | pdst[0 .. valsz] = pval[0 .. valsz]; // move value, no postblit
648 |
649 | pkey += keysz;
650 | pval += valsz;
651 | }
652 | aa.used = actualLength;
653 | return cast(ubyte*)aa;
654 | }
655 |
656 | /// compares 2 AAs for equality
657 | extern (C) int _aaEqual(scope const TypeInfo tiRaw, scope const ubyte* _aa1, scope const ubyte* _aa2)
658 | {
659 | AA aa1 = cast(AA)_aa1;
660 | AA aa2 = cast(AA)_aa2;
661 | if (aa1 is aa2)
662 | return true;
663 |
664 | immutable len = _aaLen(aa1);
665 | if (len != _aaLen(aa2))
666 | return false;
667 |
668 | if (!len) // both empty
669 | return true;
670 |
671 | import core.arsd.objectutils;
672 |
673 | auto uti = unqualify(tiRaw); //unqualify
674 | auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
675 | // compare the entries
676 | immutable off = aa1.valoff;
677 | foreach (b1; aa1.buckets)
678 | {
679 | if (!b1.filled)
680 | continue;
681 | auto pb2 = aa2.findSlotLookup(b1.hash, b1.entry, ti.key);
682 | if (pb2 is null || !ti.value.equals(b1.entry + off, pb2.entry + off))
683 | return false;
684 | }
685 | return true;
686 | }
687 |
688 | /// compute a hash
689 | extern (C) size_t _aaGetHash(scope const ubyte** _paa, scope const TypeInfo tiRaw) nothrow
690 | {
691 | AA* paa = cast(AA*)_paa;
692 | const AA aa = *paa;
693 |
694 | if (aa.empty)
695 | return 0;
696 |
697 |
698 | import core.arsd.objectutils;
699 | auto uti = unqualify(tiRaw);
700 | auto ti = *cast(TypeInfo_AssociativeArray*)&uti;
701 | immutable off = aa.valoff;
702 | auto keyHash = &ti.key.getHash;
703 | auto valHash = &ti.value.getHash;
704 |
705 | size_t h;
706 | foreach (b; aa.buckets)
707 | {
708 | // use addition here, so that hash is independent of element order
709 | if (b.filled)
710 | h += hashOf(valHash(b.entry + off), keyHash(b.entry));
711 | }
712 |
713 | return h;
714 | }
715 |
716 | /**
717 | * _aaRange implements a ForwardRange
718 | */
719 | struct Range
720 | {
721 | ubyte* impl;
722 | size_t idx;
723 | alias impl this;
724 | }
725 |
726 | extern (C) pure nothrow @nogc @trusted
727 | {
728 | Range _aaRange(return scope ubyte* _aa)
729 | {
730 | AA aa = cast(AA)_aa;
731 | if (!aa)
732 | return Range();
733 |
734 | foreach (i; aa.firstUsed .. aa.dim)
735 | {
736 | if (aa.buckets[i].filled)
737 | return Range(cast(ubyte*)aa, i);
738 | }
739 | return Range(cast(ubyte*)aa, aa.dim);
740 | }
741 |
742 | bool _aaRangeEmpty(Range r)
743 | {
744 | return r.impl is null || r.idx >= (cast(Impl*)r.impl).dim;
745 | }
746 |
747 | void* _aaRangeFrontKey(Range r)
748 | {
749 | assert(!_aaRangeEmpty(r));
750 | if (r.idx >= (cast(Impl*)r.impl).dim)
751 | return null;
752 | return (cast(Impl*)r.impl).buckets[r.idx].entry;
753 | }
754 |
755 | void* _aaRangeFrontValue(Range r)
756 | {
757 | Impl* ri = cast(Impl*)r.impl;
758 | assert(!_aaRangeEmpty(r));
759 | if (r.idx >= ri.dim)
760 | return null;
761 |
762 | auto entry = ri.buckets[r.idx].entry;
763 | return entry is null ?
764 | null :
765 | (() @trusted { return entry + ri.valoff; } ());
766 | }
767 |
768 | void _aaRangePopFront(ref Range r)
769 | {
770 | Impl* ri = (cast(Impl*)r.impl);
771 | if (r.idx >= ri.dim) return;
772 | for (++r.idx; r.idx < ri.dim; ++r.idx)
773 | {
774 | if (ri.buckets[r.idx].filled)
775 | break;
776 | }
777 | }
778 | }
779 |
--------------------------------------------------------------------------------
/arsd-webassembly/core/arsd/memory_allocation.d:
--------------------------------------------------------------------------------
1 | module core.arsd.memory_allocation;
2 |
3 |
4 |
5 | private __gshared ubyte* nextFree;
6 | private __gshared size_t memorySize; // in units of 64 KB pages
7 |
8 | // ldc defines this, used to find where wasm memory begins
9 | private extern extern(C) ubyte __heap_base;
10 | // ---unused--- -- stack grows down -- -- heap here --
11 | // this is less than __heap_base. memory map 0 ... __data_end ... __heap_base ... end of memory
12 | private extern extern(C) ubyte __data_end;
13 |
14 | // llvm intrinsics {
15 | /+
16 | mem must be 0 (it is index of memory thing)
17 | delta is in 64 KB pages
18 | return OLD size in 64 KB pages, or size_t.max if it failed.
19 | +/
20 | pragma(LDC_intrinsic, "llvm.wasm.memory.grow.i32")
21 | private int llvm_wasm_memory_grow(int mem, int delta);
22 |
23 |
24 | // in 64 KB pages
25 | pragma(LDC_intrinsic, "llvm.wasm.memory.size.i32")
26 | private int llvm_wasm_memory_size(int mem);
27 | // }
28 | // debug
29 | void printBlockDebugInfo(AllocatedBlock* block) {
30 | import std.stdio;
31 | writeln(block.blockSize, " ", block.flags, " ", block.checkChecksum() ? "OK" : "X", " ");
32 | if(block.checkChecksum())
33 | writeln(cast(size_t)((cast(ubyte*) (block + 2)) + block.blockSize), " ", block.file, " : ", block.line);
34 | }
35 |
36 |
37 | // debug
38 | export extern(C) void printBlockDebugInfo(void* ptr) {
39 | if(ptr is null) {
40 | foreach(block; AllocatedBlock) {
41 | printBlockDebugInfo(block);
42 | }
43 | return;
44 | }
45 |
46 | // otherwise assume it is a pointer returned from malloc
47 |
48 | auto block = (cast(AllocatedBlock*) ptr) - 1;
49 | if(ptr is null)
50 | block = cast(AllocatedBlock*) &__heap_base;
51 |
52 | printBlockDebugInfo(block);
53 | }
54 |
55 |
56 | export extern(C) ubyte* bridge_malloc(size_t sz) {
57 | return malloc(sz).ptr;
58 | }
59 |
60 |
61 |
62 | align(16)
63 | struct AllocatedBlock {
64 | enum Magic = 0x731a_9bec;
65 | enum Flags {
66 | inUse = 1,
67 | unique = 2,
68 | }
69 |
70 | size_t blockSize;
71 | size_t flags;
72 | size_t magic;
73 | size_t checksum;
74 |
75 | size_t used; // the amount actually requested out of the block; used for assumeSafeAppend
76 |
77 | /* debug */
78 | string file;
79 | size_t line;
80 |
81 | // note this struct MUST align each alloc on an 8 byte boundary or JS is gonna throw bullshit
82 |
83 | void populateChecksum() {
84 | checksum = blockSize ^ magic;
85 | }
86 |
87 | bool checkChecksum() const @nogc {
88 | return magic == Magic && checksum == (blockSize ^ magic);
89 | }
90 |
91 | ubyte[] dataSlice() return {
92 | return ((cast(ubyte*) &this) + typeof(this).sizeof)[0 .. blockSize];
93 | }
94 |
95 | static int opApply(scope int delegate(AllocatedBlock*) dg) {
96 | if(nextFree is null)
97 | return 0;
98 | ubyte* next = &__heap_base;
99 | AllocatedBlock* block = cast(AllocatedBlock*) next;
100 | while(block.checkChecksum()) {
101 | if(auto result = dg(block))
102 | return result;
103 | next += AllocatedBlock.sizeof;
104 | next += block.blockSize;
105 | block = cast(AllocatedBlock*) next;
106 | }
107 |
108 | return 0;
109 | }
110 | }
111 |
112 | static assert(AllocatedBlock.sizeof % 16 == 0);
113 |
114 |
115 |
116 | private bool growMemoryIfNeeded(size_t sz) @trusted {
117 | if(cast(size_t) nextFree + AllocatedBlock.sizeof + sz >= memorySize * 64*1024) {
118 | if(llvm_wasm_memory_grow(0, 4) == size_t.max)
119 | assert(0, "Out of memory"); // out of memory
120 |
121 | memorySize = llvm_wasm_memory_size(0);
122 |
123 | return true;
124 | }
125 |
126 | return false;
127 | }
128 |
129 | void free(ubyte* ptr) @nogc @trusted {
130 | auto block = (cast(AllocatedBlock*) ptr) - 1;
131 | if(!block.checkChecksum())
132 | assert(false, "Could not check block on free");
133 |
134 | block.used = 0;
135 | block.flags = 0;
136 |
137 | // last one
138 | if(ptr + block.blockSize == nextFree) {
139 | nextFree = cast(ubyte*) block;
140 | assert(cast(size_t)nextFree % 16 == 0);
141 | }
142 | }
143 |
144 |
145 | ubyte[] malloc(size_t sz, string file = __FILE__, size_t line = __LINE__) @trusted {
146 | // lol bumping that pointer
147 | if(nextFree is null) {
148 | nextFree = &__heap_base; // seems to be 75312
149 | assert(cast(size_t)nextFree % 16 == 0);
150 | memorySize = llvm_wasm_memory_size(0);
151 | }
152 |
153 | while(growMemoryIfNeeded(sz)) {}
154 |
155 | auto base = cast(AllocatedBlock*) nextFree;
156 |
157 | auto blockSize = sz;
158 | if(auto val = blockSize % 16)
159 | blockSize += 16 - val; // does NOT include this metadata section!
160 |
161 | // debug list allocations
162 | //import std.stdio; writeln(file, ":", line, " / ", sz, " +", blockSize);
163 |
164 | base.blockSize = blockSize;
165 | base.flags = AllocatedBlock.Flags.inUse;
166 | // these are just to make it more reliable to detect this header by backtracking through the pointer from a random array.
167 | // otherwise it'd prolly follow the linked list from the beginning every time or make a free list or something. idk tbh.
168 | base.magic = AllocatedBlock.Magic;
169 | base.populateChecksum();
170 |
171 | base.used = sz;
172 |
173 | // debug
174 | base.file = file;
175 | base.line = line;
176 |
177 | nextFree += AllocatedBlock.sizeof;
178 |
179 | auto ret = nextFree;
180 |
181 | nextFree += blockSize;
182 |
183 | //writeln(cast(size_t) nextFree);
184 | //import std.stdio; writeln(cast(size_t) ret, " of ", sz, " rounded to ", blockSize);
185 | //writeln(file, ":", line);
186 | assert(cast(size_t) ret % 8 == 0);
187 |
188 | return ret[0 .. sz];
189 | }
190 |
191 |
192 | ubyte[] calloc(size_t count, size_t size, string file = __FILE__, size_t line = __LINE__) @trusted
193 | {
194 | auto ret = malloc(count*size,file,line);
195 | ret[0..$] = 0;
196 | return ret;
197 | }
198 |
199 |
200 | ubyte[] realloc(ubyte* ptr, size_t newSize, string file = __FILE__, size_t line = __LINE__) @trusted {
201 | if(ptr is null)
202 | return malloc(newSize, file, line);
203 |
204 | auto block = (cast(AllocatedBlock*) ptr) - 1;
205 | if(!block.checkChecksum())
206 | assert(false, "Could not check block while realloc");
207 |
208 | // block.populateChecksum();
209 | if(newSize <= block.blockSize) {
210 | block.used = newSize;
211 | return ptr[0 .. newSize];
212 | } else {
213 | // FIXME: see if we can extend teh block into following free space before resorting to malloc
214 |
215 | if(ptr + block.blockSize == nextFree) {
216 | while(growMemoryIfNeeded(newSize)) {}
217 |
218 | size_t blockSize = newSize;
219 | if(const over = blockSize % 16)
220 | blockSize+= 16 - over;
221 |
222 | block.blockSize = blockSize;
223 | block.used = newSize;
224 | block.populateChecksum();
225 | nextFree = ptr + block.blockSize;
226 | assert(cast(size_t)nextFree % 16 == 0);
227 | return ptr[0 .. newSize];
228 | }
229 |
230 | auto newThing = malloc(newSize);
231 | newThing[0 .. block.used] = ptr[0 .. block.used];
232 |
233 | if(block.flags & AllocatedBlock.Flags.unique) {
234 | // if we do malloc, this means we are allowed to free the existing block
235 | free(ptr);
236 | }
237 |
238 | assert(cast(size_t) newThing.ptr % 16 == 0);
239 |
240 | return newThing;
241 | }
242 | }
243 |
244 | /**
245 | * If the ptr isn't owned by the runtime, it will completely malloc the data (instead of realloc)
246 | * and copy its old content.
247 | */
248 | ubyte[] realloc(ubyte[] ptr, size_t newSize, string file = __FILE__, size_t line = __LINE__) @trusted
249 | {
250 | if(ptr is null)
251 | return malloc(newSize, file, line);
252 | auto block = (cast(AllocatedBlock*) ptr) - 1;
253 | if(!block.checkChecksum())
254 | {
255 | auto ret = malloc(newSize, file, line);
256 | ret[0..ptr.length] = ptr[]; //Don't clear ptr memory as it can't be clear.
257 | return ret;
258 | }
259 | else return realloc(ptr.ptr, newSize, file, line);
260 | }
--------------------------------------------------------------------------------
/arsd-webassembly/core/arsd/objectutils.d:
--------------------------------------------------------------------------------
1 | module core.arsd.objectutils;
2 |
3 | ///Provides only __doPostblit and hasPostblit for making the code simpler.
4 | size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
5 | {
6 | if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast
7 | {
8 | auto sti = cast(TypeInfo_Struct)cast(void*)ti;
9 | if (sti.xdtor)
10 | return size_t.sizeof;
11 | }
12 | return 0;
13 | }
14 | // strip const/immutable/shared/inout from type info
15 | inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
16 | {
17 | TypeInfo ti = cast() cti;
18 | while (ti)
19 | {
20 | // avoid dynamic type casts
21 | auto tti = typeid(ti);
22 | if (tti is typeid(TypeInfo_Const))
23 | ti = (cast(TypeInfo_Const)cast(void*)ti).base;
24 | else if (tti is typeid(TypeInfo_Invariant))
25 | ti = (cast(TypeInfo_Invariant)cast(void*)ti).base;
26 | else if (tti is typeid(TypeInfo_Shared))
27 | ti = (cast(TypeInfo_Shared)cast(void*)ti).base;
28 | else if (tti is typeid(TypeInfo_Inout))
29 | ti = (cast(TypeInfo_Inout)cast(void*)ti).base;
30 | else
31 | break;
32 | }
33 | return ti;
34 | }
35 |
36 | bool hasPostblit(in TypeInfo ti) nothrow pure
37 | {
38 | return (&ti.postblit).funcptr !is &TypeInfo.postblit;
39 | }
40 |
41 | void __doPostblit(void *ptr, size_t len, const TypeInfo ti)
42 | {
43 | if (!hasPostblit(ti))
44 | return;
45 |
46 | if (auto tis = cast(TypeInfo_Struct)ti)
47 | {
48 | // this is a struct, check the xpostblit member
49 | auto pblit = tis.xpostblit;
50 | if (!pblit)
51 | // postblit not specified, no point in looping.
52 | return;
53 |
54 | // optimized for struct, call xpostblit directly for each element
55 | immutable size = ti.size;
56 | const eptr = ptr + len;
57 | for (;ptr < eptr;ptr += size)
58 | pblit(ptr);
59 | }
60 | else
61 | {
62 | // generic case, call the typeinfo's postblit function
63 | immutable size = ti.size;
64 | const eptr = ptr + len;
65 | for (;ptr < eptr;ptr += size)
66 | ti.postblit(ptr);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/arsd-webassembly/core/arsd/utf_decoding.d:
--------------------------------------------------------------------------------
1 | module core.arsd.utf_decoding;
2 |
3 |
4 | import core.internal.utf : decode, toUTF8;
5 |
6 | /**********************************************/
7 | /* 1 argument versions */
8 |
9 | /**
10 | Delegate type corresponding to transformed loop body
11 |
12 | The parameter is a pointer to the current `char`, `wchar` or `dchar`
13 |
14 | Returns: non-zero when a `break` statement is hit
15 | */
16 | extern (D) alias dg_t = int delegate(void* c);
17 |
18 | // Note: dg is extern(D), but _aApplycd() is extern(C)
19 |
20 | /**
21 | Loop over a string while changing the UTF encoding
22 |
23 | There are 6 combinations of conversions between `char`, `wchar`, and `dchar`,
24 | and 2 of each of those.
25 |
26 | The naming convention is as follows:
27 |
28 | _aApply{c,d,w}{c,d,w}{1,2}
29 |
30 | The first letter corresponds to the input string encoding, and the second letter corresponds to the target character type.
31 |
32 | - c = `char`
33 | - w = `wchar`
34 | - d = `dchar`
35 |
36 | The `1` variant only produces the character, the `2` variant also produces a loop index.
37 |
38 | Examples:
39 | ---
40 | void main()
41 | {
42 | string str;
43 | wtring wstr;
44 | dstring dstr;
45 |
46 | foreach (dchar c; str) {}
47 | // _aApplycd1
48 |
49 | foreach (wchar c; dstr) {}
50 | // _aApplydw1
51 |
52 | foreach (i, wchar c; str) {}
53 | // _aApplycw2
54 |
55 | foreach (wchar w; wstr) {}
56 | // no conversion
57 | }
58 | ---
59 |
60 | Params:
61 | aa = input string
62 | dg = foreach body transformed into a delegate, similar to `opApply`
63 |
64 | Returns:
65 | non-zero when the loop was exited through a `break`
66 | */
67 | extern (C) int _aApplycd1(in char[] aa, dg_t dg)
68 | {
69 | int result;
70 | size_t len = aa.length;
71 |
72 | debug(apply) printf("_aApplycd1(), len = %d\n", len);
73 | for (size_t i = 0; i < len; )
74 | {
75 | dchar d = aa[i];
76 | if (d & 0x80)
77 | d = decode(aa, i);
78 | else
79 | ++i;
80 | result = dg(cast(void *)&d);
81 | if (result)
82 | break;
83 | }
84 | return result;
85 | }
86 |
87 |
88 |
89 | /// ditto
90 | extern (C) int _aApplywd1(in wchar[] aa, dg_t dg)
91 | {
92 | int result;
93 | size_t len = aa.length;
94 |
95 | debug(apply) printf("_aApplywd1(), len = %d\n", len);
96 | for (size_t i = 0; i < len; )
97 | {
98 | dchar d = aa[i];
99 | if (d >= 0xD800)
100 | d = decode(aa, i);
101 | else
102 | ++i;
103 | result = dg(cast(void *)&d);
104 | if (result)
105 | break;
106 | }
107 | return result;
108 | }
109 |
110 |
111 | /// ditto
112 | extern (C) int _aApplycw1(in char[] aa, dg_t dg)
113 | {
114 | int result;
115 | size_t len = aa.length;
116 |
117 | debug(apply) printf("_aApplycw1(), len = %d\n", len);
118 | for (size_t i = 0; i < len; )
119 | {
120 | wchar w = aa[i];
121 | if (w & 0x80)
122 | {
123 | dchar d = decode(aa, i);
124 | if (d <= 0xFFFF)
125 | w = cast(wchar) d;
126 | else
127 | {
128 | w = cast(wchar)((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
129 | result = dg(cast(void *)&w);
130 | if (result)
131 | break;
132 | w = cast(wchar)(((d - 0x10000) & 0x3FF) + 0xDC00);
133 | }
134 | }
135 | else
136 | ++i;
137 | result = dg(cast(void *)&w);
138 | if (result)
139 | break;
140 | }
141 | return result;
142 | }
143 |
144 |
145 | /// ditto
146 | extern (C) int _aApplywc1(in wchar[] aa, dg_t dg)
147 | {
148 | int result;
149 | size_t len = aa.length;
150 |
151 | debug(apply) printf("_aApplywc1(), len = %d\n", len);
152 | for (size_t i = 0; i < len; )
153 | {
154 | wchar w = aa[i];
155 | if (w & ~0x7F)
156 | {
157 | char[4] buf = void;
158 |
159 | dchar d = decode(aa, i);
160 | auto b = toUTF8(buf, d);
161 | foreach (char c2; b)
162 | {
163 | result = dg(cast(void *)&c2);
164 | if (result)
165 | return result;
166 | }
167 | }
168 | else
169 | {
170 | char c = cast(char)w;
171 | ++i;
172 | result = dg(cast(void *)&c);
173 | if (result)
174 | break;
175 | }
176 | }
177 | return result;
178 | }
179 |
180 |
181 | /// ditto
182 | extern (C) int _aApplydc1(in dchar[] aa, dg_t dg)
183 | {
184 | int result;
185 |
186 | debug(apply) printf("_aApplydc1(), len = %d\n", aa.length);
187 | foreach (dchar d; aa)
188 | {
189 | if (d & ~0x7F)
190 | {
191 | char[4] buf = void;
192 |
193 | auto b = toUTF8(buf, d);
194 | foreach (char c2; b)
195 | {
196 | result = dg(cast(void *)&c2);
197 | if (result)
198 | return result;
199 | }
200 | }
201 | else
202 | {
203 | char c = cast(char)d;
204 | result = dg(cast(void *)&c);
205 | if (result)
206 | break;
207 | }
208 | }
209 | return result;
210 | }
211 |
212 |
213 | /// ditto
214 | extern (C) int _aApplydw1(in dchar[] aa, dg_t dg)
215 | {
216 | int result;
217 |
218 | debug(apply) printf("_aApplydw1(), len = %d\n", aa.length);
219 | foreach (dchar d; aa)
220 | {
221 | wchar w;
222 |
223 | if (d <= 0xFFFF)
224 | w = cast(wchar) d;
225 | else
226 | {
227 | w = cast(wchar)((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
228 | result = dg(cast(void *)&w);
229 | if (result)
230 | break;
231 | w = cast(wchar)(((d - 0x10000) & 0x3FF) + 0xDC00);
232 | }
233 | result = dg(cast(void *)&w);
234 | if (result)
235 | break;
236 | }
237 | return result;
238 | }
239 |
240 |
241 | /****************************************************************************/
242 | /* 2 argument versions */
243 |
244 | /**
245 | Delegate type corresponding to transformed loop body
246 |
247 | Parameters are pointers to a `size_t` loop index, and the current `char`, `wchar` or `dchar`.
248 |
249 | Returns: non-zero when a `break` statement is hit
250 | */
251 | extern (D) alias dg2_t = int delegate(void* i, void* c);
252 |
253 | // Note: dg is extern(D), but _aApplycd2() is extern(C)
254 |
255 | /**
256 | Variants of _aApplyXXX that include a loop index.
257 | */
258 | extern (C) int _aApplycd2(in char[] aa, dg2_t dg)
259 | {
260 | int result;
261 | size_t len = aa.length;
262 |
263 | debug(apply) printf("_aApplycd2(), len = %d\n", len);
264 | size_t n;
265 | for (size_t i = 0; i < len; i += n)
266 | {
267 | dchar d = aa[i];
268 | if (d & 0x80)
269 | {
270 | n = i;
271 | d = decode(aa, n);
272 | n -= i;
273 | }
274 | else
275 | n = 1;
276 | result = dg(&i, cast(void *)&d);
277 | if (result)
278 | break;
279 | }
280 | return result;
281 | }
282 |
283 | /// ditto
284 | extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg)
285 | {
286 | int result;
287 | size_t len = aa.length;
288 |
289 | debug(apply) printf("_aApplywd2(), len = %d\n", len);
290 | size_t n;
291 | for (size_t i = 0; i < len; i += n)
292 | {
293 | dchar d = aa[i];
294 | if (d & ~0x7F)
295 | {
296 | n = i;
297 | d = decode(aa, n);
298 | n -= i;
299 | }
300 | else
301 | n = 1;
302 | result = dg(&i, cast(void *)&d);
303 | if (result)
304 | break;
305 | }
306 | return result;
307 | }
308 |
309 | /// ditto
310 | extern (C) int _aApplycw2(in char[] aa, dg2_t dg)
311 | {
312 | int result;
313 | size_t len = aa.length;
314 |
315 | debug(apply) printf("_aApplycw2(), len = %d\n", len);
316 | size_t n;
317 | for (size_t i = 0; i < len; i += n)
318 | {
319 | wchar w = aa[i];
320 | if (w & 0x80)
321 | {
322 | n = i;
323 | dchar d = decode(aa, n);
324 | n -= i;
325 | if (d <= 0xFFFF)
326 | w = cast(wchar) d;
327 | else
328 | {
329 | w = cast(wchar) ((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
330 | result = dg(&i, cast(void *)&w);
331 | if (result)
332 | break;
333 | w = cast(wchar) (((d - 0x10000) & 0x3FF) + 0xDC00);
334 | }
335 | }
336 | else
337 | n = 1;
338 | result = dg(&i, cast(void *)&w);
339 | if (result)
340 | break;
341 | }
342 | return result;
343 | }
344 |
345 |
346 | /// ditto
347 | extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg)
348 | {
349 | int result;
350 | size_t len = aa.length;
351 |
352 | debug(apply) printf("_aApplywc2(), len = %d\n", len);
353 | size_t n;
354 | for (size_t i = 0; i < len; i += n)
355 | {
356 | wchar w = aa[i];
357 | if (w & ~0x7F)
358 | {
359 | char[4] buf = void;
360 |
361 | n = i;
362 | dchar d = decode(aa, n);
363 | n -= i;
364 | auto b = toUTF8(buf, d);
365 | foreach (char c2; b)
366 | {
367 | result = dg(&i, cast(void *)&c2);
368 | if (result)
369 | return result;
370 | }
371 | }
372 | else
373 | {
374 | char c = cast(char)w;
375 | n = 1;
376 | result = dg(&i, cast(void *)&c);
377 | if (result)
378 | break;
379 | }
380 | }
381 | return result;
382 | }
383 |
384 |
385 | /// ditto
386 | extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg)
387 | {
388 | int result;
389 | size_t len = aa.length;
390 |
391 | debug(apply) printf("_aApplydc2(), len = %d\n", len);
392 | for (size_t i = 0; i < len; i++)
393 | {
394 | dchar d = aa[i];
395 | if (d & ~0x7F)
396 | {
397 | char[4] buf = void;
398 |
399 | auto b = toUTF8(buf, d);
400 | foreach (char c2; b)
401 | {
402 | result = dg(&i, cast(void *)&c2);
403 | if (result)
404 | return result;
405 | }
406 | }
407 | else
408 | {
409 | char c = cast(char)d;
410 | result = dg(&i, cast(void *)&c);
411 | if (result)
412 | break;
413 | }
414 | }
415 | return result;
416 | }
417 |
418 |
419 | /// ditto
420 | extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg)
421 | { int result;
422 |
423 | debug(apply) printf("_aApplydw2(), len = %d\n", aa.length);
424 | foreach (size_t i, dchar d; aa)
425 | {
426 | wchar w;
427 | auto j = i;
428 |
429 | if (d <= 0xFFFF)
430 | w = cast(wchar) d;
431 | else
432 | {
433 | w = cast(wchar) ((((d - 0x10000) >> 10) & 0x3FF) + 0xD800);
434 | result = dg(&j, cast(void *)&w);
435 | if (result)
436 | break;
437 | w = cast(wchar) (((d - 0x10000) & 0x3FF) + 0xDC00);
438 | }
439 | result = dg(&j, cast(void *)&w);
440 | if (result)
441 | break;
442 | }
443 | return result;
444 | }
445 |
--------------------------------------------------------------------------------
/arsd-webassembly/core/internal/utf.d:
--------------------------------------------------------------------------------
1 | /********************************************
2 | * Encode and decode UTF-8, UTF-16 and UTF-32 strings.
3 | *
4 | * For Win32 systems, the C wchar_t type is UTF-16 and corresponds to the D
5 | * wchar type.
6 | * For Posix systems, the C wchar_t type is UTF-32 and corresponds to
7 | * the D utf.dchar type.
8 | *
9 | * UTF character support is restricted to (\u0000 <= character <= \U0010FFFF).
10 | *
11 | * See_Also:
12 | * $(LINK2 http://en.wikipedia.org/wiki/Unicode, Wikipedia)
13 | * $(LINK http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8)
14 | * $(LINK http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335)
15 | *
16 | * Copyright: Copyright Digital Mars 2003 - 2016.
17 | * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
18 | * Authors: Walter Bright, Sean Kelly
19 | * Source: $(DRUNTIMESRC core/internal/_utf.d)
20 | */
21 |
22 | module core.internal.utf;
23 |
24 | extern (C) void onUnicodeError( string msg, size_t idx, string file = __FILE__, size_t line = __LINE__ ) @trusted
25 | {
26 | _d_assert_msg("onUnicodeError: "~msg, file, line);
27 | }
28 |
29 | /*******************************
30 | * Test if c is a valid UTF-32 character.
31 | *
32 | * \uFFFE and \uFFFF are considered valid by this function,
33 | * as they are permitted for internal use by an application,
34 | * but they are not allowed for interchange by the Unicode standard.
35 | *
36 | * Returns: true if it is, false if not.
37 | */
38 |
39 | @safe @nogc pure nothrow
40 | bool isValidDchar(dchar c)
41 | {
42 | /* Note: FFFE and FFFF are specifically permitted by the
43 | * Unicode standard for application internal use, but are not
44 | * allowed for interchange.
45 | * (thanks to Arcane Jill)
46 | */
47 |
48 | return c < 0xD800 ||
49 | (c > 0xDFFF && c <= 0x10FFFF /*&& c != 0xFFFE && c != 0xFFFF*/);
50 | }
51 |
52 | unittest
53 | {
54 | debug(utf) printf("utf.isValidDchar.unittest\n");
55 | assert(isValidDchar(cast(dchar)'a') == true);
56 | assert(isValidDchar(cast(dchar)0x1FFFFF) == false);
57 | }
58 |
59 |
60 |
61 | static immutable UTF8stride =
62 | [
63 | cast(ubyte)
64 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
65 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
66 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
67 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
68 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
69 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
70 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
71 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
72 | 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
73 | 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
74 | 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
75 | 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
76 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
77 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
78 | 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
79 | 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0xFF,0xFF,
80 | ];
81 |
82 | /**
83 | * stride() returns the length of a UTF-8 sequence starting at index i
84 | * in string s.
85 | * Returns:
86 | * The number of bytes in the UTF-8 sequence or
87 | * 0xFF meaning s[i] is not the start of of UTF-8 sequence.
88 | */
89 | @safe @nogc pure nothrow
90 | uint stride(const scope char[] s, size_t i)
91 | {
92 | return UTF8stride[s[i]];
93 | }
94 |
95 | /**
96 | * stride() returns the length of a UTF-16 sequence starting at index i
97 | * in string s.
98 | */
99 | @safe @nogc pure nothrow
100 | uint stride(const scope wchar[] s, size_t i)
101 | { uint u = s[i];
102 | return 1 + (u >= 0xD800 && u <= 0xDBFF);
103 | }
104 |
105 | /**
106 | * stride() returns the length of a UTF-32 sequence starting at index i
107 | * in string s.
108 | * Returns: The return value will always be 1.
109 | */
110 | @safe @nogc pure nothrow
111 | uint stride(const scope dchar[] s, size_t i)
112 | {
113 | return 1;
114 | }
115 |
116 | /*******************************************
117 | * Given an index i into an array of characters s[],
118 | * and assuming that index i is at the start of a UTF character,
119 | * determine the number of UCS characters up to that index i.
120 | */
121 | @safe
122 | size_t toUCSindex(const scope char[] s, size_t i)
123 | {
124 | size_t n;
125 | size_t j;
126 |
127 | for (j = 0; j < i; )
128 | {
129 | j += stride(s, j);
130 | n++;
131 | }
132 | if (j > i)
133 | {
134 | onUnicodeError("invalid UTF-8 sequence", j);
135 | }
136 | return n;
137 | }
138 |
139 | /** ditto */
140 | @safe
141 | size_t toUCSindex(const scope wchar[] s, size_t i)
142 | {
143 | size_t n;
144 | size_t j;
145 |
146 | for (j = 0; j < i; )
147 | {
148 | j += stride(s, j);
149 | n++;
150 | }
151 | if (j > i)
152 | {
153 | onUnicodeError("invalid UTF-16 sequence", j);
154 | }
155 | return n;
156 | }
157 |
158 | /** ditto */
159 | @safe @nogc pure nothrow
160 | size_t toUCSindex(const scope dchar[] s, size_t i)
161 | {
162 | return i;
163 | }
164 |
165 | /******************************************
166 | * Given a UCS index n into an array of characters s[], return the UTF index.
167 | */
168 | @safe
169 | size_t toUTFindex(const scope char[] s, size_t n)
170 | {
171 | size_t i;
172 |
173 | while (n--)
174 | {
175 | uint j = UTF8stride[s[i]];
176 | if (j == 0xFF)
177 | onUnicodeError("invalid UTF-8 sequence", i);
178 | i += j;
179 | }
180 | return i;
181 | }
182 |
183 | /** ditto */
184 | @safe @nogc pure nothrow
185 | size_t toUTFindex(const scope wchar[] s, size_t n)
186 | {
187 | size_t i;
188 |
189 | while (n--)
190 | { wchar u = s[i];
191 |
192 | i += 1 + (u >= 0xD800 && u <= 0xDBFF);
193 | }
194 | return i;
195 | }
196 |
197 | /** ditto */
198 | @safe @nogc pure nothrow
199 | size_t toUTFindex(const scope dchar[] s, size_t n)
200 | {
201 | return n;
202 | }
203 |
204 | /* =================== Decode ======================= */
205 |
206 | /***************
207 | * Decodes and returns character starting at s[idx]. idx is advanced past the
208 | * decoded character. If the character is not well formed, a UtfException is
209 | * thrown and idx remains unchanged.
210 | */
211 | @safe
212 | dchar decode(const scope char[] s, ref size_t idx)
213 | in
214 | {
215 | assert(idx >= 0 && idx < s.length);
216 | }
217 | out (result)
218 | {
219 | assert(isValidDchar(result));
220 | }
221 | do
222 | {
223 | size_t len = s.length;
224 | dchar V;
225 | size_t i = idx;
226 | char u = s[i];
227 |
228 | if (u & 0x80)
229 | { uint n;
230 | char u2;
231 |
232 | /* The following encodings are valid, except for the 5 and 6 byte
233 | * combinations:
234 | * 0xxxxxxx
235 | * 110xxxxx 10xxxxxx
236 | * 1110xxxx 10xxxxxx 10xxxxxx
237 | * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
238 | * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
239 | * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
240 | */
241 | for (n = 1; ; n++)
242 | {
243 | if (n > 4)
244 | goto Lerr; // only do the first 4 of 6 encodings
245 | if (((u << n) & 0x80) == 0)
246 | {
247 | if (n == 1)
248 | goto Lerr;
249 | break;
250 | }
251 | }
252 |
253 | // Pick off (7 - n) significant bits of B from first byte of octet
254 | V = cast(dchar)(u & ((1 << (7 - n)) - 1));
255 |
256 | if (i + (n - 1) >= len)
257 | goto Lerr; // off end of string
258 |
259 | /* The following combinations are overlong, and illegal:
260 | * 1100000x (10xxxxxx)
261 | * 11100000 100xxxxx (10xxxxxx)
262 | * 11110000 1000xxxx (10xxxxxx 10xxxxxx)
263 | * 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx)
264 | * 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx)
265 | */
266 | u2 = s[i + 1];
267 | if ((u & 0xFE) == 0xC0 ||
268 | (u == 0xE0 && (u2 & 0xE0) == 0x80) ||
269 | (u == 0xF0 && (u2 & 0xF0) == 0x80) ||
270 | (u == 0xF8 && (u2 & 0xF8) == 0x80) ||
271 | (u == 0xFC && (u2 & 0xFC) == 0x80))
272 | goto Lerr; // overlong combination
273 |
274 | for (uint j = 1; j != n; j++)
275 | {
276 | u = s[i + j];
277 | if ((u & 0xC0) != 0x80)
278 | goto Lerr; // trailing bytes are 10xxxxxx
279 | V = (V << 6) | (u & 0x3F);
280 | }
281 | if (!isValidDchar(V))
282 | goto Lerr;
283 | i += n;
284 | }
285 | else
286 | {
287 | V = cast(dchar) u;
288 | i++;
289 | }
290 |
291 | idx = i;
292 | return V;
293 |
294 | Lerr:
295 | onUnicodeError("invalid UTF-8 sequence", i);
296 | return V; // dummy return
297 | }
298 |
299 |
300 | /** ditto */
301 | @safe
302 | dchar decode(const scope wchar[] s, ref size_t idx)
303 | in
304 | {
305 | assert(idx >= 0 && idx < s.length);
306 | }
307 | out (result)
308 | {
309 | assert(isValidDchar(result));
310 | }
311 | do
312 | {
313 | string msg;
314 | dchar V;
315 | size_t i = idx;
316 | uint u = s[i];
317 |
318 | if (u & ~0x7F)
319 | { if (u >= 0xD800 && u <= 0xDBFF)
320 | { uint u2;
321 |
322 | if (i + 1 == s.length)
323 | { msg = "surrogate UTF-16 high value past end of string";
324 | goto Lerr;
325 | }
326 | u2 = s[i + 1];
327 | if (u2 < 0xDC00 || u2 > 0xDFFF)
328 | { msg = "surrogate UTF-16 low value out of range";
329 | goto Lerr;
330 | }
331 | u = ((u - 0xD7C0) << 10) + (u2 - 0xDC00);
332 | i += 2;
333 | }
334 | else if (u >= 0xDC00 && u <= 0xDFFF)
335 | { msg = "unpaired surrogate UTF-16 value";
336 | goto Lerr;
337 | }
338 | else if (u == 0xFFFE || u == 0xFFFF)
339 | { msg = "illegal UTF-16 value";
340 | goto Lerr;
341 | }
342 | else
343 | i++;
344 | }
345 | else
346 | {
347 | i++;
348 | }
349 |
350 | idx = i;
351 | return cast(dchar)u;
352 |
353 | Lerr:
354 | onUnicodeError(msg, i);
355 | return cast(dchar)u; // dummy return
356 | }
357 |
358 | /** ditto */
359 | @safe
360 | dchar decode(const scope dchar[] s, ref size_t idx)
361 | in
362 | {
363 | assert(idx >= 0 && idx < s.length);
364 | }
365 | do
366 | {
367 | size_t i = idx;
368 | dchar c = s[i];
369 |
370 | if (!isValidDchar(c))
371 | goto Lerr;
372 | idx = i + 1;
373 | return c;
374 |
375 | Lerr:
376 | onUnicodeError("invalid UTF-32 value", i);
377 | return c; // dummy return
378 | }
379 |
380 |
381 | /* =================== Encode ======================= */
382 |
383 | /*******************************
384 | * Encodes character c and appends it to array s[].
385 | */
386 | @safe pure nothrow
387 | void encode(ref char[] s, dchar c)
388 | in
389 | {
390 | assert(isValidDchar(c));
391 | }
392 | do
393 | {
394 | char[] r = s;
395 |
396 | if (c <= 0x7F)
397 | {
398 | r ~= cast(char) c;
399 | }
400 | else
401 | {
402 | char[4] buf = void;
403 | uint L;
404 |
405 | if (c <= 0x7FF)
406 | {
407 | buf[0] = cast(char)(0xC0 | (c >> 6));
408 | buf[1] = cast(char)(0x80 | (c & 0x3F));
409 | L = 2;
410 | }
411 | else if (c <= 0xFFFF)
412 | {
413 | buf[0] = cast(char)(0xE0 | (c >> 12));
414 | buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
415 | buf[2] = cast(char)(0x80 | (c & 0x3F));
416 | L = 3;
417 | }
418 | else if (c <= 0x10FFFF)
419 | {
420 | buf[0] = cast(char)(0xF0 | (c >> 18));
421 | buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
422 | buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
423 | buf[3] = cast(char)(0x80 | (c & 0x3F));
424 | L = 4;
425 | }
426 | else
427 | {
428 | assert(0);
429 | }
430 | r ~= buf[0 .. L];
431 | }
432 | s = r;
433 | }
434 |
435 | unittest
436 | {
437 | debug(utf) printf("utf.encode.unittest\n");
438 |
439 | char[] s = "abcd".dup;
440 | encode(s, cast(dchar)'a');
441 | assert(s.length == 5);
442 | assert(s == "abcda");
443 |
444 | encode(s, cast(dchar)'\u00A9');
445 | assert(s.length == 7);
446 | assert(s == "abcda\xC2\xA9");
447 | //assert(s == "abcda\u00A9"); // BUG: fix compiler
448 |
449 | encode(s, cast(dchar)'\u2260');
450 | assert(s.length == 10);
451 | assert(s == "abcda\xC2\xA9\xE2\x89\xA0");
452 | }
453 |
454 | /** ditto */
455 | @safe pure nothrow
456 | void encode(ref wchar[] s, dchar c)
457 | in
458 | {
459 | assert(isValidDchar(c));
460 | }
461 | do
462 | {
463 | wchar[] r = s;
464 |
465 | if (c <= 0xFFFF)
466 | {
467 | r ~= cast(wchar) c;
468 | }
469 | else
470 | {
471 | wchar[2] buf = void;
472 |
473 | buf[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
474 | buf[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
475 | r ~= buf;
476 | }
477 | s = r;
478 | }
479 |
480 | /** ditto */
481 | @safe pure nothrow
482 | void encode(ref dchar[] s, dchar c)
483 | in
484 | {
485 | assert(isValidDchar(c));
486 | }
487 | do
488 | {
489 | s ~= c;
490 | }
491 |
492 | /**
493 | Returns the code length of $(D c) in the encoding using $(D C) as a
494 | code point. The code is returned in character count, not in bytes.
495 | */
496 | @safe pure nothrow @nogc
497 | ubyte codeLength(C)(dchar c)
498 | {
499 | static if (C.sizeof == 1)
500 | {
501 | if (c <= 0x7F) return 1;
502 | if (c <= 0x7FF) return 2;
503 | if (c <= 0xFFFF) return 3;
504 | if (c <= 0x10FFFF) return 4;
505 | assert(false);
506 | }
507 | else static if (C.sizeof == 2)
508 | {
509 | return c <= 0xFFFF ? 1 : 2;
510 | }
511 | else
512 | {
513 | static assert(C.sizeof == 4);
514 | return 1;
515 | }
516 | }
517 |
518 | /* =================== Validation ======================= */
519 |
520 | /***********************************
521 | Checks to see if string is well formed or not. $(D S) can be an array
522 | of $(D char), $(D wchar), or $(D dchar). Returns $(D false) if it is not.
523 | Use to check all untrusted input for correctness.
524 | */
525 | @safe
526 | bool isValidString(S)(const scope S s)
527 | {
528 | auto len = s.length;
529 | for (size_t i = 0; i < len; )
530 | {
531 | decode(s, i);
532 | }
533 |
534 | return true;
535 | }
536 |
537 | /* =================== Conversion to UTF8 ======================= */
538 |
539 | @safe nothrow @nogc
540 | char[] toUTF8(return scope char[] buf, dchar c)
541 | in
542 | {
543 | assert(isValidDchar(c));
544 | }
545 | do
546 | {
547 | if (c <= 0x7F)
548 | {
549 | buf[0] = cast(char) c;
550 | return buf[0 .. 1];
551 | }
552 | else if (c <= 0x7FF)
553 | {
554 | buf[0] = cast(char)(0xC0 | (c >> 6));
555 | buf[1] = cast(char)(0x80 | (c & 0x3F));
556 | return buf[0 .. 2];
557 | }
558 | else if (c <= 0xFFFF)
559 | {
560 | buf[0] = cast(char)(0xE0 | (c >> 12));
561 | buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
562 | buf[2] = cast(char)(0x80 | (c & 0x3F));
563 | return buf[0 .. 3];
564 | }
565 | else if (c <= 0x10FFFF)
566 | {
567 | buf[0] = cast(char)(0xF0 | (c >> 18));
568 | buf[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
569 | buf[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
570 | buf[3] = cast(char)(0x80 | (c & 0x3F));
571 | return buf[0 .. 4];
572 | }
573 | assert(0);
574 | }
575 |
576 | /*******************
577 | * Encodes string s into UTF-8 and returns the encoded string.
578 | */
579 | @safe
580 | string toUTF8(return scope string s)
581 | in
582 | {
583 | assert(isValidString(s));
584 | }
585 | do
586 | {
587 | return s;
588 | }
589 |
590 | /** ditto */
591 | @trusted
592 | string toUTF8(const scope wchar[] s)
593 | {
594 | char[] r;
595 | size_t i;
596 | size_t slen = s.length;
597 |
598 | r.length = slen;
599 |
600 | for (i = 0; i < slen; i++)
601 | { wchar c = s[i];
602 |
603 | if (c <= 0x7F)
604 | r[i] = cast(char)c; // fast path for ascii
605 | else
606 | {
607 | r.length = i;
608 | foreach (dchar ch; s[i .. slen])
609 | {
610 | encode(r, ch);
611 | }
612 | break;
613 | }
614 | }
615 | return cast(string)r;
616 | }
617 |
618 | /** ditto */
619 | @trusted
620 | string toUTF8(const scope dchar[] s)
621 | {
622 | char[] r;
623 | size_t i;
624 | size_t slen = s.length;
625 |
626 | r.length = slen;
627 |
628 | for (i = 0; i < slen; i++)
629 | { dchar c = s[i];
630 |
631 | if (c <= 0x7F)
632 | r[i] = cast(char)c; // fast path for ascii
633 | else
634 | {
635 | r.length = i;
636 | foreach (dchar d; s[i .. slen])
637 | {
638 | encode(r, d);
639 | }
640 | break;
641 | }
642 | }
643 | return cast(string)r;
644 | }
645 |
646 | /* =================== Conversion to UTF16 ======================= */
647 |
648 | @safe pure nothrow @nogc
649 | wchar[] toUTF16(return scope wchar[] buf, dchar c)
650 | in
651 | {
652 | assert(isValidDchar(c));
653 | }
654 | do
655 | {
656 | if (c <= 0xFFFF)
657 | {
658 | buf[0] = cast(wchar) c;
659 | return buf[0 .. 1];
660 | }
661 | else
662 | {
663 | buf[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
664 | buf[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00);
665 | return buf[0 .. 2];
666 | }
667 | }
668 |
669 | /****************
670 | * Encodes string s into UTF-16 and returns the encoded string.
671 | * toUTF16z() is suitable for calling the 'W' functions in the Win32 API that take
672 | * an LPWSTR or LPCWSTR argument.
673 | */
674 | @trusted
675 | wstring toUTF16(const scope char[] s)
676 | {
677 | wchar[] r;
678 | size_t slen = s.length;
679 |
680 | if (!__ctfe)
681 | {
682 | // Reserve still does a lot if slen is zero.
683 | // Return early for that case.
684 | if (0 == slen)
685 | return ""w;
686 | r.reserve(slen);
687 | }
688 | for (size_t i = 0; i < slen; )
689 | {
690 | dchar c = s[i];
691 | if (c <= 0x7F)
692 | {
693 | i++;
694 | r ~= cast(wchar)c;
695 | }
696 | else
697 | {
698 | c = decode(s, i);
699 | encode(r, c);
700 | }
701 | }
702 | return cast(wstring)r;
703 | }
704 |
705 | alias const(wchar)* wptr;
706 | /** ditto */
707 | @trusted
708 | wptr toUTF16z(const scope char[] s)
709 | {
710 | wchar[] r;
711 | size_t slen = s.length;
712 |
713 | if (!__ctfe)
714 | {
715 | // Reserve still does a lot if slen is zero.
716 | // Return early for that case.
717 | if (0 == slen)
718 | return &"\0"w[0];
719 | r.reserve(slen + 1);
720 | }
721 | for (size_t i = 0; i < slen; )
722 | {
723 | dchar c = s[i];
724 | if (c <= 0x7F)
725 | {
726 | i++;
727 | r ~= cast(wchar)c;
728 | }
729 | else
730 | {
731 | c = decode(s, i);
732 | encode(r, c);
733 | }
734 | }
735 | r ~= '\000';
736 | return &r[0];
737 | }
738 |
739 | /** ditto */
740 | @safe
741 | wstring toUTF16(return scope wstring s)
742 | in
743 | {
744 | assert(isValidString(s));
745 | }
746 | do
747 | {
748 | return s;
749 | }
750 |
751 | /** ditto */
752 | @trusted
753 | wstring toUTF16(const scope dchar[] s)
754 | {
755 | wchar[] r;
756 | size_t slen = s.length;
757 |
758 | if (!__ctfe)
759 | {
760 | // Reserve still does a lot if slen is zero.
761 | // Return early for that case.
762 | if (0 == slen)
763 | return ""w;
764 | r.reserve(slen);
765 | }
766 | for (size_t i = 0; i < slen; i++)
767 | {
768 | encode(r, s[i]);
769 | }
770 | return cast(wstring)r;
771 | }
772 |
773 | /* =================== Conversion to UTF32 ======================= */
774 |
775 | /*****
776 | * Encodes string s into UTF-32 and returns the encoded string.
777 | */
778 | @trusted
779 | dstring toUTF32(const scope char[] s)
780 | {
781 | dchar[] r;
782 | size_t slen = s.length;
783 | size_t j = 0;
784 |
785 | r.length = slen; // r[] will never be longer than s[]
786 | for (size_t i = 0; i < slen; )
787 | {
788 | dchar c = s[i];
789 | if (c >= 0x80)
790 | c = decode(s, i);
791 | else
792 | i++; // c is ascii, no need for decode
793 | r[j++] = c;
794 | }
795 | return cast(dstring)r[0 .. j];
796 | }
797 |
798 | /** ditto */
799 | @trusted
800 | dstring toUTF32(const scope wchar[] s)
801 | {
802 | dchar[] r;
803 | size_t slen = s.length;
804 | size_t j = 0;
805 |
806 | r.length = slen; // r[] will never be longer than s[]
807 | for (size_t i = 0; i < slen; )
808 | {
809 | dchar c = s[i];
810 | if (c >= 0x80)
811 | c = decode(s, i);
812 | else
813 | i++; // c is ascii, no need for decode
814 | r[j++] = c;
815 | }
816 | return cast(dstring)r[0 .. j];
817 | }
818 |
819 | /** ditto */
820 | @safe
821 | dstring toUTF32(return scope dstring s)
822 | in
823 | {
824 | assert(isValidString(s));
825 | }
826 | do
827 | {
828 | return s;
829 | }
830 |
831 | /* ================================ tests ================================== */
832 |
833 | unittest
834 | {
835 | debug(utf) printf("utf.toUTF.unittest\n");
836 |
837 | auto c = "hello"c[];
838 | auto w = toUTF16(c);
839 | assert(w == "hello");
840 | auto d = toUTF32(c);
841 | assert(d == "hello");
842 |
843 | c = toUTF8(w);
844 | assert(c == "hello");
845 | d = toUTF32(w);
846 | assert(d == "hello");
847 |
848 | c = toUTF8(d);
849 | assert(c == "hello");
850 | w = toUTF16(d);
851 | assert(w == "hello");
852 |
853 |
854 | c = "hel\u1234o";
855 | w = toUTF16(c);
856 | assert(w == "hel\u1234o");
857 | d = toUTF32(c);
858 | assert(d == "hel\u1234o");
859 |
860 | c = toUTF8(w);
861 | assert(c == "hel\u1234o");
862 | d = toUTF32(w);
863 | assert(d == "hel\u1234o");
864 |
865 | c = toUTF8(d);
866 | assert(c == "hel\u1234o");
867 | w = toUTF16(d);
868 | assert(w == "hel\u1234o");
869 |
870 |
871 | c = "he\U000BAAAAllo";
872 | w = toUTF16(c);
873 | //foreach (wchar c; w) printf("c = x%x\n", c);
874 | //foreach (wchar c; cast(wstring)"he\U000BAAAAllo") printf("c = x%x\n", c);
875 | assert(w == "he\U000BAAAAllo");
876 | d = toUTF32(c);
877 | assert(d == "he\U000BAAAAllo");
878 |
879 | c = toUTF8(w);
880 | assert(c == "he\U000BAAAAllo");
881 | d = toUTF32(w);
882 | assert(d == "he\U000BAAAAllo");
883 |
884 | c = toUTF8(d);
885 | assert(c == "he\U000BAAAAllo");
886 | w = toUTF16(d);
887 | assert(w == "he\U000BAAAAllo");
888 |
889 | wchar[2] buf;
890 | auto ret = toUTF16(buf, '\U000BAAAA');
891 | assert(ret == "\U000BAAAA");
892 | }
893 |
--------------------------------------------------------------------------------
/arsd-webassembly/object.d:
--------------------------------------------------------------------------------
1 | // Minimal druntime for webassembly. Assumes your program has a main function.
2 | module object;
3 |
4 | static import arsd.webassembly;
5 |
6 | version(CarelessAlocation)
7 | {
8 | version = inline_concat;
9 | }
10 |
11 | import core.arsd.memory_allocation;
12 |
13 | alias noreturn = typeof(*null);
14 | alias string = immutable(char)[];
15 | alias wstring = immutable(wchar)[];
16 | alias dstring = immutable(dchar)[];
17 | alias size_t = uint;
18 | alias ptrdiff_t = int;
19 |
20 |
21 | // then the entry point just for convenience so main works.
22 | extern(C) int _Dmain(string[] args);
23 | export extern(C) void _start() { _Dmain(null); }
24 |
25 | extern(C) bool _xopEquals(in void*, in void*) { return false; } // assert(0);
26 |
27 | // basic array support {
28 |
29 | template _arrayOp(Args...)
30 | {
31 | import core.internal.array.operations;
32 | alias _arrayOp = arrayOp!Args;
33 | }
34 |
35 | extern(C) void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t srclen, size_t elemsz) {
36 | auto d = cast(ubyte*) dst;
37 | auto s = cast(ubyte*) src;
38 | auto len = dstlen * elemsz;
39 |
40 | while(len) {
41 | *d = *s;
42 | d++;
43 | s++;
44 | len--;
45 | }
46 |
47 | }
48 |
49 | void reserve(T)(ref T[] arr, size_t length) @trusted {
50 | arr = (cast(T*) (malloc(length * T.sizeof).ptr))[0 .. 0];
51 | }
52 |
53 |
54 | extern(C) void _d_arraybounds(string file, size_t line) {
55 | arsd.webassembly.eval(
56 | q{ console.error("Range error: " + $0 + ":" + $1 )},
57 | file, line);
58 | arsd.webassembly.abort();
59 | }
60 |
61 |
62 | /// Called when an out of range slice of an array is created
63 | extern(C) void _d_arraybounds_slice(string file, uint line, size_t lwr, size_t upr, size_t length)
64 | {
65 | arsd.webassembly.eval(
66 | q{ console.error("Range error: " + $0 + ":" + $1 + " [" + $2 + ".." + $3 + "] <> " + $4)},
67 | file, line, lwr, upr, length);
68 | arsd.webassembly.abort();
69 | }
70 |
71 | /// Called when an out of range array index is accessed
72 | extern(C) void _d_arraybounds_index(string file, uint line, size_t index, size_t length)
73 | {
74 | arsd.webassembly.eval(
75 | q{ console.error("Array index " + $0 + " out of bounds '[0.."+$1+"]' " + $2 + ":" + $3)},
76 | index, length, file, line);
77 | arsd.webassembly.abort();
78 | }
79 |
80 |
81 | extern(C) void* memset(void* s, int c, size_t n) @nogc nothrow pure
82 | {
83 | auto d = cast(ubyte*) s;
84 | while(n) {
85 | *d = cast(ubyte) c;
86 | d++;
87 | n--;
88 | }
89 | return s;
90 | }
91 |
92 | pragma(LDC_intrinsic, "llvm.memcpy.p0i8.p0i8.i#")
93 | void llvm_memcpy(T)(void* dst, const(void)* src, T len, bool volatile_ = false);
94 |
95 | extern(C) void *memcpy(void* dest, const(void)* src, size_t n) pure @nogc nothrow
96 | {
97 | ubyte *d = cast(ubyte*) dest;
98 | const (ubyte) *s = cast(const(ubyte)*)src;
99 | for (; n; n--) *d++ = *s++;
100 | return dest;
101 | }
102 |
103 | extern(C) int memcmp(const(void)* s1, const(void*) s2, size_t n) pure @nogc nothrow @trusted
104 | {
105 | auto b = cast(ubyte*) s1;
106 | auto b2 = cast(ubyte*) s2;
107 | foreach(i; 0 .. n) {
108 | if(auto diff = *b - *b2)
109 | return diff;
110 | b++;
111 | b2++;
112 | }
113 | return 0;
114 | }
115 |
116 | public import core.arsd.utf_decoding;
117 |
118 | // }
119 |
120 | extern(C) void _d_assert(string file, uint line) @trusted @nogc pure
121 | {
122 | arsd.webassembly.eval(q{ console.error("Assert failure: " + $0 + ":" + $1); /*, "[" + $2 + ".." + $3 + "] <> " + $4);*/ }, file, line);//, lwr, upr, length);
123 | arsd.webassembly.abort();
124 | }
125 | void _d_assertp(immutable(char)* file, uint line)
126 | {
127 | // import core.stdc.string : strlen;
128 | size_t sz = 0;
129 | while(file[sz] != '\0') sz++;
130 | arsd.webassembly.eval(q{ console.error("Assert failure: " + $0 + ":" + $1 + "(" + $2 + ")"); /*, "[" + $2 + ".." + $3 + "] <> " + $4);*/ }, file[0 .. sz], line);//, lwr, upr, length);
131 | arsd.webassembly.abort();
132 | }
133 |
134 |
135 | extern(C) void _d_assert_msg(string msg, string file, uint line) @trusted @nogc pure
136 | {
137 | arsd.webassembly.eval(q{ console.error("Assert failure: " + $0 + ":" + $1 + "(" + $2 + ")"); /*, "[" + $2 + ".." + $3 + "] <> " + $4);*/ }, file, line, msg);//, lwr, upr, length);
138 | arsd.webassembly.abort();
139 | }
140 |
141 | void __switch_error(string file, size_t line) @trusted @nogc pure
142 | {
143 | _d_assert_msg("final switch error",file, line);
144 | }
145 |
146 | bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs) {
147 | if (lhs.length != rhs.length) {
148 | return false;
149 | }
150 | foreach(i; 0..lhs.length) {
151 | if (lhs[i] != rhs[i]) {
152 | return false;
153 | }
154 | }
155 | return true;
156 | }
157 |
158 | // bare basics class support {
159 |
160 |
161 | extern(C) Object _d_allocclass(TypeInfo_Class ti) {
162 | auto ptr = malloc(ti.m_init.length);
163 | ptr[] = ti.m_init[];
164 | return cast(Object) ptr.ptr;
165 | }
166 |
167 | extern(C) void* _d_dynamic_cast(Object o, TypeInfo_Class c) {
168 | void* res = null;
169 | size_t offset = 0;
170 | if (o && _d_isbaseof2(typeid(o), c, offset))
171 | {
172 | res = cast(void*) o + offset;
173 | }
174 | return res;
175 | }
176 |
177 | /*************************************
178 | * Attempts to cast Object o to class c.
179 | * Returns o if successful, null if not.
180 | */
181 | extern(C) void* _d_interface_cast(void* p, TypeInfo_Class c)
182 | {
183 | if (!p)
184 | return null;
185 |
186 | Interface* pi = **cast(Interface***) p;
187 | return _d_dynamic_cast(cast(Object)(p - pi.offset), c);
188 | }
189 |
190 |
191 | extern(C)
192 | int _d_isbaseof2(scope TypeInfo_Class oc, scope const TypeInfo_Class c, scope ref size_t offset) @safe
193 |
194 | {
195 | if (oc is c)
196 | return true;
197 |
198 | do
199 | {
200 | if (oc.base is c)
201 | return true;
202 |
203 | // Bugzilla 2013: Use depth-first search to calculate offset
204 | // from the derived (oc) to the base (c).
205 | foreach (iface; oc.interfaces)
206 | {
207 | if (iface.classinfo is c || _d_isbaseof2(iface.classinfo, c, offset))
208 | {
209 | offset += iface.offset;
210 | return true;
211 | }
212 | }
213 |
214 | oc = oc.base;
215 | } while (oc);
216 |
217 | return false;
218 | }
219 |
220 | int __cmp(T)(scope const T[] lhs, scope const T[] rhs) @trusted pure @nogc nothrow
221 | if (__traits(isScalar, T))
222 | {
223 | // Compute U as the implementation type for T
224 | static if (is(T == ubyte) || is(T == void) || is(T == bool))
225 | alias U = char;
226 | else static if (is(T == wchar))
227 | alias U = ushort;
228 | else static if (is(T == dchar))
229 | alias U = uint;
230 | else static if (is(T == ifloat))
231 | alias U = float;
232 | else static if (is(T == idouble))
233 | alias U = double;
234 | else static if (is(T == ireal))
235 | alias U = real;
236 | else
237 | alias U = T;
238 |
239 | static if (is(U == char))
240 | {
241 | int dstrcmp(scope const char[] s1, scope const char[] s2 ) @trusted pure @nogc nothrow
242 | {
243 | immutable len = s1.length <= s2.length ? s1.length : s2.length;
244 | if (__ctfe)
245 | {
246 | foreach (const u; 0 .. len)
247 | {
248 | if (s1[u] != s2[u])
249 | return s1[u] > s2[u] ? 1 : -1;
250 | }
251 | }
252 | else
253 | {
254 | const ret = memcmp( s1.ptr, s2.ptr, len );
255 | if ( ret )
256 | return ret;
257 | }
258 | return (s1.length > s2.length) - (s1.length < s2.length);
259 | }
260 | return dstrcmp(cast(char[]) lhs, cast(char[]) rhs);
261 | }
262 | else static if (!is(U == T))
263 | {
264 | // Reuse another implementation
265 | return __cmp(cast(U[]) lhs, cast(U[]) rhs);
266 | }
267 | else
268 | {
269 | version (BigEndian)
270 | static if (__traits(isUnsigned, T) ? !is(T == __vector) : is(T : P*, P))
271 | {
272 | if (!__ctfe)
273 | {
274 | import core.stdc.string : memcmp;
275 | int c = memcmp(lhs.ptr, rhs.ptr, (lhs.length <= rhs.length ? lhs.length : rhs.length) * T.sizeof);
276 | if (c)
277 | return c;
278 | static if (size_t.sizeof <= uint.sizeof && T.sizeof >= 2)
279 | return cast(int) lhs.length - cast(int) rhs.length;
280 | else
281 | return int(lhs.length > rhs.length) - int(lhs.length < rhs.length);
282 | }
283 | }
284 |
285 | immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length;
286 | foreach (const u; 0 .. len)
287 | {
288 | auto a = lhs.ptr[u], b = rhs.ptr[u];
289 | static if (is(T : creal))
290 | {
291 | // Use rt.cmath2._Ccmp instead ?
292 | // Also: if NaN is present, numbers will appear equal.
293 | auto r = (a.re > b.re) - (a.re < b.re);
294 | if (!r) r = (a.im > b.im) - (a.im < b.im);
295 | }
296 | else
297 | {
298 | // This pattern for three-way comparison is better than conditional operators
299 | // See e.g. https://godbolt.org/z/3j4vh1
300 | const r = (a > b) - (a < b);
301 | }
302 | if (r) return r;
303 | }
304 | return (lhs.length > rhs.length) - (lhs.length < rhs.length);
305 | }
306 | }
307 |
308 | // This function is called by the compiler when dealing with array
309 | // comparisons in the semantic analysis phase of CmpExp. The ordering
310 | // comparison is lowered to a call to this template.
311 | int __cmp(T1, T2)(T1[] s1, T2[] s2)
312 | if (!__traits(isScalar, T1) && !__traits(isScalar, T2))
313 | {
314 | import core.internal.traits : Unqual;
315 | alias U1 = Unqual!T1;
316 | alias U2 = Unqual!T2;
317 |
318 | static if (is(U1 == void) && is(U2 == void))
319 | static @trusted ref inout(ubyte) at(inout(void)[] r, size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; }
320 | else
321 | static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; }
322 |
323 | // All unsigned byte-wide types = > dstrcmp
324 | immutable len = s1.length <= s2.length ? s1.length : s2.length;
325 |
326 | foreach (const u; 0 .. len)
327 | {
328 | static if (__traits(compiles, __cmp(at(s1, u), at(s2, u))))
329 | {
330 | auto c = __cmp(at(s1, u), at(s2, u));
331 | if (c != 0)
332 | return c;
333 | }
334 | else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u))))
335 | {
336 | auto c = at(s1, u).opCmp(at(s2, u));
337 | if (c != 0)
338 | return c;
339 | }
340 | else static if (__traits(compiles, at(s1, u) < at(s2, u)))
341 | {
342 | if (int result = (at(s1, u) > at(s2, u)) - (at(s1, u) < at(s2, u)))
343 | return result;
344 | }
345 | else
346 | {
347 | // TODO: fix this legacy bad behavior, see
348 | // https://issues.dlang.org/show_bug.cgi?id=17244
349 | static assert(is(U1 == U2), "Internal error.");
350 | import core.stdc.string : memcmp;
351 | auto c = (() @trusted => memcmp(&at(s1, u), &at(s2, u), U1.sizeof))();
352 | if (c != 0)
353 | return c;
354 | }
355 | }
356 | return (s1.length > s2.length) - (s1.length < s2.length);
357 | }
358 |
359 |
360 |
361 | /**
362 | Support for switch statements switching on strings.
363 | Params:
364 | caseLabels = sorted array of strings generated by compiler. Note the
365 | strings are sorted by length first, and then lexicographically.
366 | condition = string to look up in table
367 | Returns:
368 | index of match in caseLabels, a negative integer if not found
369 | */
370 | int __switch(T, caseLabels...)(/*in*/ const scope T[] condition) pure nothrow @safe @nogc
371 | {
372 | // This closes recursion for other cases.
373 | static if (caseLabels.length == 0)
374 | {
375 | return int.min;
376 | }
377 | else static if (caseLabels.length == 1)
378 | {
379 | return __cmp(condition, caseLabels[0]) == 0 ? 0 : int.min;
380 | }
381 | // To be adjusted after measurements
382 | // Compile-time inlined binary search.
383 | else static if (caseLabels.length < 7)
384 | {
385 | int r = void;
386 | enum mid = cast(int)caseLabels.length / 2;
387 | if (condition.length == caseLabels[mid].length)
388 | {
389 | r = __cmp(condition, caseLabels[mid]);
390 | if (r == 0) return mid;
391 | }
392 | else
393 | {
394 | // Equivalent to (but faster than) condition.length > caseLabels[$ / 2].length ? 1 : -1
395 | r = ((condition.length > caseLabels[mid].length) << 1) - 1;
396 | }
397 |
398 | if (r < 0)
399 | {
400 | // Search the left side
401 | return __switch!(T, caseLabels[0 .. mid])(condition);
402 | }
403 | else
404 | {
405 | // Search the right side
406 | return __switch!(T, caseLabels[mid + 1 .. $])(condition) + mid + 1;
407 | }
408 | }
409 | else
410 | {
411 | // Need immutable array to be accessible in pure code, but case labels are
412 | // currently coerced to the switch condition type (e.g. const(char)[]).
413 | pure @trusted nothrow @nogc asImmutable(scope const(T[])[] items)
414 | {
415 | assert(__ctfe); // only @safe for CTFE
416 | immutable T[][caseLabels.length] result = cast(immutable)(items[]);
417 | return result;
418 | }
419 | static immutable T[][caseLabels.length] cases = asImmutable([caseLabels]);
420 |
421 | // Run-time binary search in a static array of labels.
422 | return __switchSearch!T(cases[], condition);
423 | }
424 | }
425 |
426 | // binary search in sorted string cases, also see `__switch`.
427 | private int __switchSearch(T)(/*in*/ const scope T[][] cases, /*in*/ const scope T[] condition) pure nothrow @safe @nogc
428 | {
429 | size_t low = 0;
430 | size_t high = cases.length;
431 |
432 | do
433 | {
434 | auto mid = (low + high) / 2;
435 | int r = void;
436 | if (condition.length == cases[mid].length)
437 | {
438 | r = __cmp(condition, cases[mid]);
439 | if (r == 0) return cast(int) mid;
440 | }
441 | else
442 | {
443 | // Generates better code than "expr ? 1 : -1" on dmd and gdc, same with ldc
444 | r = ((condition.length > cases[mid].length) << 1) - 1;
445 | }
446 |
447 | if (r > 0) low = mid + 1;
448 | else high = mid;
449 | }
450 | while (low < high);
451 |
452 | // Not found
453 | return -1;
454 | }
455 |
456 | //TODO: Support someday?
457 | extern(C) void _d_throw_exception(Throwable o)
458 | {
459 | assert(false, "Exception throw");
460 | }
461 |
462 |
463 | // for closures
464 | extern(C) void* _d_allocmemory(size_t sz) {
465 | return malloc(sz).ptr;
466 | }
467 |
468 | ///For POD structures
469 | extern (C) void* _d_allocmemoryT(TypeInfo ti)
470 | {
471 | return malloc(ti.size).ptr;
472 | }
473 |
474 |
475 | class Object
476 | {
477 | /// Convert Object to human readable string
478 | string toString() { return "Object"; }
479 | /// Compute hash function for Object
480 | size_t toHash() @trusted nothrow
481 | {
482 | auto addr = cast(size_t)cast(void*)this;
483 | return addr ^ (addr >>> 4);
484 | }
485 |
486 | /// Compare against another object. NOT IMPLEMENTED!
487 | int opCmp(Object o) { assert(false, "not implemented"); }
488 | /// Check equivalence againt another object
489 | bool opEquals(Object o) { return this is o; }
490 | }
491 |
492 | /// Compare to objects
493 | bool opEquals(Object lhs, Object rhs)
494 | {
495 | // If aliased to the same object or both null => equal
496 | if (lhs is rhs) return true;
497 |
498 | // If either is null => non-equal
499 | if (lhs is null || rhs is null) return false;
500 |
501 | if (!lhs.opEquals(rhs)) return false;
502 |
503 | // If same exact type => one call to method opEquals
504 | if (typeid(lhs) is typeid(rhs) ||
505 | !__ctfe && typeid(lhs).opEquals(typeid(rhs)))
506 | /* CTFE doesn't like typeid much. 'is' works, but opEquals doesn't
507 | (issue 7147). But CTFE also guarantees that equal TypeInfos are
508 | always identical. So, no opEquals needed during CTFE. */
509 | {
510 | return true;
511 | }
512 |
513 | // General case => symmetric calls to method opEquals
514 | return rhs.opEquals(lhs);
515 | }
516 | /************************
517 | * Returns true if lhs and rhs are equal.
518 | */
519 | bool opEquals(const Object lhs, const Object rhs)
520 | {
521 | // A hack for the moment.
522 | return opEquals(cast()lhs, cast()rhs);
523 | }
524 |
525 | class TypeInfo
526 | {
527 | override string toString() const @safe nothrow
528 | {
529 | return typeid(this).name;
530 | }
531 |
532 | const(TypeInfo) next()nothrow pure inout @nogc { return null; }
533 | size_t size() nothrow pure const @safe @nogc { return 0; }
534 |
535 | bool equals(in void* p1, in void* p2) const { return p1 == p2; }
536 |
537 | override size_t toHash() @trusted const nothrow
538 | {
539 | return hashOf(this.toString());
540 | }
541 |
542 |
543 | size_t getHash(scope const void* p) @trusted nothrow const
544 | {
545 | return hashOf(p);
546 | }
547 |
548 | /**
549 | * Return default initializer. If the type should be initialized to all
550 | * zeros, an array with a null ptr and a length equal to the type size will
551 | * be returned. For static arrays, this returns the default initializer for
552 | * a single element of the array, use `tsize` to get the correct size.
553 | */
554 | const(void)[] initializer() const @trusted nothrow pure
555 | {
556 | return (cast(const(void)*) null)[0 .. typeof(null).sizeof];
557 | }
558 |
559 | @property uint flags() nothrow pure const @safe @nogc { return 0; }
560 | /// Run the destructor on the object and all its sub-objects
561 | void destroy(void* p) const {}
562 | /// Run the postblit on the object and all its sub-objects
563 | void postblit(void* p) const {}
564 |
565 | @property size_t talign() nothrow pure const { return size; }
566 | }
567 |
568 | class TypeInfo_Class : TypeInfo
569 | {
570 | ubyte[] m_init; /// class static initializer (length gives class size)
571 | string name; /// name of class
572 | void*[] vtbl; // virtual function pointer table
573 | Interface[] interfaces;
574 | TypeInfo_Class base;
575 | void* destructor;
576 | void function(Object) classInvariant;
577 | uint flags;
578 | void* deallocator;
579 | void*[] offTi;
580 | void function(Object) defaultConstructor;
581 | immutable(void)* rtInfo;
582 |
583 | override @property size_t size() nothrow pure const
584 | { return Object.sizeof; }
585 |
586 | override size_t getHash(scope const void* p) @trusted const
587 | {
588 | auto o = *cast(Object*)p;
589 | return o ? o.toHash() : 0;
590 | }
591 |
592 | override bool equals(in void* p1, in void* p2) const
593 | {
594 | Object o1 = *cast(Object*)p1;
595 | Object o2 = *cast(Object*)p2;
596 |
597 | return (o1 is o2) || (o1 && o1.opEquals(o2));
598 | }
599 |
600 | override const(void)[] initializer() nothrow pure const @safe
601 | {
602 | return m_init;
603 | }
604 | }
605 |
606 | void destroy(bool initialize = true, T)(ref T obj) if (is(T == struct))
607 | {
608 | import core.internal.destruction : destructRecurse;
609 |
610 | destructRecurse(obj);
611 |
612 | static if (initialize)
613 | {
614 | import core.internal.lifetime : emplaceInitializer;
615 | emplaceInitializer(obj); // emplace T.init
616 | }
617 | }
618 |
619 | private extern (D) nothrow alias void function (Object) fp_t;
620 | private extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) nothrow
621 | {
622 | auto ppv = cast(void**) p;
623 | if (!p || !*ppv)
624 | return;
625 |
626 | auto pc = cast(TypeInfo_Class*) *ppv;
627 | if (det)
628 | {
629 | auto c = *pc;
630 | do
631 | {
632 | if (c.destructor)
633 | (cast(fp_t) c.destructor)(cast(Object) p); // call destructor
634 | }
635 | while ((c = c.base) !is null);
636 | }
637 |
638 | if (resetMemory)
639 | {
640 | auto w = (*pc).initializer;
641 | p[0 .. w.length] = w[];
642 | }
643 | *ppv = null; // zero vptr even if `resetMemory` is false
644 | }
645 | extern(C) void _d_callfinalizer(void* p)
646 | {
647 | rt_finalize2(p);
648 | }
649 |
650 | void destroy(bool initialize = true, T)(T obj) if (is(T == class))
651 | {
652 | static if (__traits(getLinkage, T) == "C++")
653 | {
654 | static if (__traits(hasMember, T, "__xdtor"))
655 | obj.__xdtor();
656 |
657 | static if (initialize)
658 | {
659 | const initializer = __traits(initSymbol, T);
660 | (cast(void*)obj)[0 .. initializer.length] = initializer[];
661 | }
662 | }
663 | else
664 | {
665 | // Bypass overloaded opCast
666 | auto ptr = (() @trusted => *cast(void**) &obj)();
667 | rt_finalize2(ptr, true, initialize);
668 | }
669 | }
670 | void destroy(bool initialize = true, T)(T obj) if (is(T == interface))
671 | {
672 | static assert(__traits(getLinkage, T) == "D", "Invalid call to destroy() on extern(" ~ __traits(getLinkage, T) ~ ") interface");
673 |
674 | destroy!initialize(cast(Object)obj);
675 | }
676 | void destroy(bool initialize = true, T)(ref T obj)
677 | if (!is(T == struct) && !is(T == interface) && !is(T == class) && !__traits(isStaticArray, T))
678 | {
679 | static if (initialize)
680 | obj = T.init;
681 | }
682 |
683 |
684 | class TypeInfo_Pointer : TypeInfo
685 | {
686 | TypeInfo m_next;
687 |
688 | override bool equals(in void* p1, in void* p2) const { return *cast(void**)p1 == *cast(void**)p2; }
689 | override size_t getHash(scope const void* p) @trusted const
690 | {
691 | size_t addr = cast(size_t) *cast(const void**)p;
692 | return addr ^ (addr >> 4);
693 | }
694 | override @property size_t size() nothrow pure const { return (void*).sizeof; }
695 |
696 | override const(void)[] initializer() const @trusted { return (cast(void *)null)[0 .. (void*).sizeof]; }
697 |
698 | override const (TypeInfo) next() const { return m_next; }
699 | }
700 |
701 | class TypeInfo_Array : TypeInfo {
702 | TypeInfo value;
703 | override size_t size() const { return (void[]).sizeof; }
704 | override const(TypeInfo) next() const { return value; }
705 |
706 | override bool equals(in void* p1, in void* p2) const
707 | {
708 | void[] a1 = *cast(void[]*)p1;
709 | void[] a2 = *cast(void[]*)p2;
710 | if (a1.length != a2.length)
711 | return false;
712 | size_t sz = value.size;
713 | for (size_t i = 0; i < a1.length; i++)
714 | {
715 | if (!value.equals(a1.ptr + i * sz, a2.ptr + i * sz))
716 | return false;
717 | }
718 | return true;
719 | }
720 | override @property size_t talign() nothrow pure const
721 | {
722 | return (void[]).alignof;
723 | }
724 | override const(void)[] initializer() const @trusted { return (cast(void *)null)[0 .. (void[]).sizeof]; }
725 | }
726 |
727 | class TypeInfo_StaticArray : TypeInfo {
728 | TypeInfo value;
729 | size_t len;
730 | override size_t size() const { return value.size * len; }
731 | override const(TypeInfo) next() const { return value; }
732 |
733 | override bool equals(in void* p1, in void* p2) const {
734 | size_t sz = value.size;
735 |
736 | for (size_t u = 0; u < len; u++)
737 | {
738 | if (!value.equals(p1 + u * sz, p2 + u * sz))
739 | {
740 | return false;
741 | }
742 | }
743 | return true;
744 | }
745 | override @property size_t talign() nothrow pure const
746 | {
747 | return value.talign;
748 | }
749 |
750 | }
751 |
752 | import core.arsd.aa;
753 | alias AARange = core.arsd.aa.Range;
754 | extern (C)
755 | {
756 | // from druntime/src/rt/aaA.d
757 | /* The real type is (non-importable) `rt.aaA.Impl*`;
758 | * the compiler uses `void*` for its prototypes.
759 | */
760 | private alias AA = void*;
761 |
762 | // size_t _aaLen(in AA aa) pure nothrow @nogc;
763 | private void* _aaGetY(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey) pure nothrow;
764 | private void* _aaGetX(scope AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey, out bool found) ;
765 | // inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz, in void* pkey);
766 | inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz, const TypeInfo tiValueArray) ;
767 | inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) ;
768 | void* _aaRehash(AA* paa, const scope TypeInfo keyti) ;
769 | void _aaClear(AA aa) ;
770 |
771 | // alias _dg_t = extern(D) int delegate(void*);
772 | // int _aaApply(AA aa, size_t keysize, _dg_t dg);
773 |
774 | // alias _dg2_t = extern(D) int delegate(void*, void*);
775 | // int _aaApply2(AA aa, size_t keysize, _dg2_t dg);
776 |
777 | AARange _aaRange(AA aa) pure nothrow @nogc @safe;
778 | bool _aaRangeEmpty(AARange r) pure @safe @nogc nothrow;
779 | void* _aaRangeFrontKey(AARange r);
780 | void* _aaRangeFrontValue(AARange r) pure @nogc nothrow;
781 | void _aaRangePopFront(ref AARange r) pure @nogc nothrow @safe;
782 |
783 | int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2);
784 | size_t _aaGetHash(scope const AA* aa, scope const TypeInfo tiRaw) nothrow;
785 |
786 | /*
787 | _d_assocarrayliteralTX marked as pure, because aaLiteral can be called from pure code.
788 | This is a typesystem hole, however this is existing hole.
789 | Early compiler didn't check purity of toHash or postblit functions, if key is a UDT thus
790 | copiler allowed to create AA literal with keys, which have impure unsafe toHash methods.
791 | */
792 | void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] values);
793 | }
794 |
795 | private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe
796 | {
797 | // ensure we are dealing with a genuine AA.
798 | static if (is(const(V[K]) == const(T)))
799 | alias realAA = aa;
800 | else
801 | const(V[K]) realAA = aa;
802 | return _aaRange(() @trusted { return *cast(AA*)&realAA; } ());
803 | }
804 |
805 | auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
806 | {
807 | import core.internal.traits : substInout;
808 |
809 | static struct Result
810 | {
811 | AARange r;
812 |
813 | pure nothrow @nogc:
814 | @property bool empty() @safe { return _aaRangeEmpty(r); }
815 | @property ref front() @trusted
816 | {
817 | return *cast(substInout!K*) _aaRangeFrontKey(r);
818 | }
819 | void popFront() @safe { _aaRangePopFront(r); }
820 | @property Result save() { return this; }
821 | }
822 |
823 | return Result(_aaToRange(aa));
824 | }
825 |
826 | /** ditto */
827 | auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc
828 | {
829 | return (*aa).byKey();
830 | }
831 |
832 |
833 |
834 | auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
835 | {
836 | import core.internal.traits : substInout;
837 |
838 | static struct Result
839 | {
840 | AARange r;
841 |
842 | pure nothrow @nogc:
843 | @property bool empty() @safe { return _aaRangeEmpty(r); }
844 | @property ref front() @trusted
845 | {
846 | return *cast(substInout!V*) _aaRangeFrontValue(r);
847 | }
848 | void popFront() @safe { _aaRangePopFront(r); }
849 | @property Result save() { return this; }
850 | }
851 |
852 | return Result(_aaToRange(aa));
853 | }
854 |
855 | /** ditto */
856 | auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
857 | {
858 | return (*aa).byValue();
859 | }
860 |
861 | Key[] keys(T : Value[Key], Value, Key)(T aa) @property
862 | {
863 | // ensure we are dealing with a genuine AA.
864 | static if (is(const(Value[Key]) == const(T)))
865 | alias realAA = aa;
866 | else
867 | const(Value[Key]) realAA = aa;
868 | auto res = () @trusted {
869 | auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[]));
870 | return *cast(Key[]*)&a;
871 | }();
872 | static if (__traits(hasPostblit, Key))
873 | _doPostblit(res);
874 | return res;
875 | }
876 |
877 | /** ditto */
878 | Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
879 | {
880 | return (*aa).keys;
881 | }
882 |
883 | /***********************************
884 | * Returns a newly allocated dynamic array containing a copy of the values from
885 | * the associative array.
886 | * Params:
887 | * aa = The associative array.
888 | * Returns:
889 | * A dynamic array containing a copy of the values.
890 | */
891 | Value[] values(T : Value[Key], Value, Key)(T aa) @property
892 | {
893 | // ensure we are dealing with a genuine AA.
894 | static if (is(const(Value[Key]) == const(T)))
895 | alias realAA = aa;
896 | else
897 | const(Value[Key]) realAA = aa;
898 | auto res = () @trusted {
899 | auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[]));
900 | return *cast(Value[]*)&a;
901 | }();
902 | static if (__traits(hasPostblit, Value))
903 | _doPostblit(res);
904 | return res;
905 | }
906 |
907 | /** ditto */
908 | Value[] values(T : Value[Key], Value, Key)(T *aa) @property
909 | {
910 | return (*aa).values;
911 | }
912 | inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue)
913 | {
914 | auto p = key in aa;
915 | return p ? *p : defaultValue;
916 | }
917 |
918 | /** ditto */
919 | inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
920 | {
921 | return (*aa).get(key, defaultValue);
922 | }
923 | // Tests whether T can be @safe-ly copied. Use a union to exclude destructor from the test.
924 | private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x; auto u = U(*x); }));
925 |
926 | /***********************************
927 | * Looks up key; if it exists applies the update callable else evaluates the
928 | * create callable and adds it to the associative array
929 | * Params:
930 | * aa = The associative array.
931 | * key = The key.
932 | * create = The callable to apply on create.
933 | * update = The callable to apply on update.
934 | */
935 | void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update)
936 | if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void)))
937 | {
938 | bool found;
939 | // if key is @safe-ly copyable, `update` may infer @safe
940 | static if (isSafeCopyable!K)
941 | {
942 | auto p = () @trusted
943 | {
944 | return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
945 | } ();
946 | }
947 | else
948 | {
949 | auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
950 | }
951 | if (!found)
952 | *p = create();
953 | else
954 | {
955 | static if (is(typeof(update(*p)) == void))
956 | update(*p);
957 | else
958 | *p = update(*p);
959 | }
960 | }
961 |
962 | ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
963 | {
964 | bool found;
965 | // if key is @safe-ly copyable, `require` can infer @safe
966 | static if (isSafeCopyable!K)
967 | {
968 | auto p = () @trusted
969 | {
970 | return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
971 | } ();
972 | }
973 | else
974 | {
975 | auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
976 | }
977 | if (found)
978 | return *p;
979 | else
980 | {
981 | *p = value; // Not `return (*p = value)` since if `=` is overloaded
982 | return *p; // this might not return a ref to the left-hand side.
983 | }
984 | }
985 |
986 |
987 |
988 | /***********************************
989 | * Removes all remaining keys and values from an associative array.
990 | * Params:
991 | * aa = The associative array.
992 | */
993 | void clear(Value, Key)(Value[Key] aa)
994 | {
995 | _aaClear(*cast(AA *) &aa);
996 | }
997 |
998 | /** ditto */
999 | void clear(Value, Key)(Value[Key]* aa)
1000 | {
1001 | _aaClear(*cast(AA *) aa);
1002 | }
1003 | void* aaLiteral(Key, Value)(Key[] keys, Value[] values) @trusted pure
1004 | {
1005 | return _d_assocarrayliteralTX(typeid(Value[Key]), *cast(void[]*)&keys, *cast(void[]*)&values);
1006 | }
1007 |
1008 | alias AssociativeArray(Key, Value) = Value[Key];
1009 |
1010 | class TypeInfo_AssociativeArray : TypeInfo
1011 | {
1012 | override string toString() const
1013 | {
1014 | return value.toString() ~ "[" ~ key.toString() ~ "]";
1015 | }
1016 |
1017 | override bool opEquals(Object o)
1018 | {
1019 | if (this is o)
1020 | return true;
1021 | auto c = cast(const TypeInfo_AssociativeArray)o;
1022 | return c && this.key == c.key &&
1023 | this.value == c.value;
1024 | }
1025 |
1026 | override bool equals(in void* p1, in void* p2) @trusted const
1027 | {
1028 | return !!_aaEqual(this, *cast(const AA*) p1, *cast(const AA*) p2);
1029 | }
1030 |
1031 | override size_t getHash(scope const void* p) nothrow @trusted const
1032 | {
1033 | return _aaGetHash(cast(AA*)p, this);
1034 | }
1035 |
1036 | // BUG: need to add the rest of the functions
1037 |
1038 | override @property size_t size() nothrow pure const
1039 | {
1040 | return (char[int]).sizeof;
1041 | }
1042 |
1043 | override const(void)[] initializer() const @trusted
1044 | {
1045 | return (cast(void *)null)[0 .. (char[int]).sizeof];
1046 | }
1047 |
1048 | override @property inout(TypeInfo) next() nothrow pure inout { return value; }
1049 | override @property uint flags() nothrow pure const { return 1; }
1050 |
1051 |
1052 | TypeInfo value;
1053 | TypeInfo key;
1054 |
1055 | override @property size_t talign() nothrow pure const
1056 | {
1057 | return (char[int]).alignof;
1058 | }
1059 |
1060 | version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
1061 | {
1062 | arg1 = typeid(void*);
1063 | return 0;
1064 | }
1065 | }
1066 |
1067 |
1068 |
1069 | class TypeInfo_Enum : TypeInfo {
1070 | TypeInfo base;
1071 | string name;
1072 | void[] m_init;
1073 |
1074 | override size_t size() const { return base.size; }
1075 | override const(TypeInfo) next() const { return base.next; }
1076 | override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); }
1077 | override @property size_t talign() const { return base.talign; }
1078 | override void destroy(void* p) const { return base.destroy(p); }
1079 | override void postblit(void* p) const { return base.postblit(p); }
1080 |
1081 | override const(void)[] initializer() const
1082 | {
1083 | return m_init.length ? m_init : base.initializer();
1084 | }
1085 | }
1086 |
1087 | extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length)
1088 | {
1089 | return malloc(length * ti.next.size);
1090 | }
1091 |
1092 | extern(C) void[] _d_newarrayT(const TypeInfo ti, size_t length)
1093 | {
1094 | auto arr = _d_newarrayU(ti, length);
1095 | (cast(byte[])arr)[] = 0;
1096 | return arr;
1097 | }
1098 |
1099 | extern(C) void[] _d_newarrayiT(const TypeInfo ti, size_t length)
1100 | {
1101 | auto result = _d_newarrayU(ti, length);
1102 | auto tinext = ti.next;
1103 | auto size = tinext.size;
1104 | auto init = tinext.initializer();
1105 | switch (init.length)
1106 | {
1107 | foreach (T; AliasSeq!(ubyte, ushort, uint, ulong))
1108 | {
1109 | case T.sizeof:
1110 | if (tinext.talign % T.alignof == 0)
1111 | {
1112 | (cast(T*)result.ptr)[0 .. size * length / T.sizeof] = *cast(T*)init.ptr;
1113 | return result;
1114 | }
1115 | goto default;
1116 | }
1117 |
1118 | default:
1119 | {
1120 | immutable sz = init.length;
1121 | for (size_t u = 0; u < size * length; u += sz)
1122 | {
1123 | memcpy(result.ptr + u, init.ptr, sz);
1124 | }
1125 | return result;
1126 | }
1127 | }
1128 | }
1129 |
1130 | extern (C) void* _d_newitemU(scope const TypeInfo _ti)
1131 | {
1132 | import core.arsd.objectutils;
1133 | auto ti = cast()_ti;
1134 | immutable tiSize = structTypeInfoSize(ti);
1135 | immutable itemSize = ti.size;
1136 | immutable size = itemSize + tiSize;
1137 | auto p = malloc(size);
1138 |
1139 | return p.ptr;
1140 | }
1141 |
1142 | /// ditto
1143 | extern (C) void* _d_newitemT(in TypeInfo _ti)
1144 | {
1145 | auto p = _d_newitemU(_ti);
1146 | memset(p, 0, _ti.size);
1147 | return p;
1148 | }
1149 |
1150 | /// Same as above, for item with non-zero initializer.
1151 | extern (C) void* _d_newitemiT(in TypeInfo _ti)
1152 | {
1153 | auto p = _d_newitemU(_ti);
1154 | auto init = _ti.initializer();
1155 | assert(init.length <= _ti.size);
1156 | memcpy(p, init.ptr, init.length);
1157 | return p;
1158 | }
1159 |
1160 |
1161 |
1162 | private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dimensions)
1163 | {
1164 | if (dimensions.length == 0)
1165 | return null;
1166 |
1167 | void[] foo(const TypeInfo ti, size_t[] dimensions)
1168 | {
1169 | size_t count = dimensions[0];
1170 |
1171 | if (dimensions.length == 1)
1172 | {
1173 | auto r = op(ti, count);
1174 | return (*cast(void[]*)(&r))[0..count];
1175 | }
1176 | void[] p = malloc((void[]).sizeof * count);
1177 |
1178 | foreach (i; 0..count)
1179 | {
1180 | (cast(void[]*)p.ptr)[i] = foo(ti.next, dimensions[1..$]);
1181 | }
1182 | return p[0..count];
1183 | }
1184 |
1185 | return foo(ti, dimensions);
1186 | }
1187 |
1188 |
1189 | extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims)
1190 | {
1191 | if (dims.length == 0)
1192 | return null;
1193 | else
1194 | return _d_newarrayOpT!(_d_newarrayT)(ti, dims);
1195 | }
1196 |
1197 | /// ditto
1198 | extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims)
1199 | {
1200 | if (dims.length == 0)
1201 | return null;
1202 | else
1203 | return _d_newarrayOpT!(_d_newarrayiT)(ti, dims);
1204 | }
1205 |
1206 |
1207 |
1208 |
1209 | AllocatedBlock* getAllocatedBlock(void* ptr) {
1210 | auto block = (cast(AllocatedBlock*) ptr) - 1;
1211 | if(!block.checkChecksum())
1212 | return null;
1213 | return block;
1214 | }
1215 |
1216 | /++
1217 | Marks the memory block as OK to append in-place if possible.
1218 | +/
1219 | void assumeSafeAppend(T)(T[] arr) {
1220 | auto block = getAllocatedBlock(arr.ptr);
1221 | if(block is null) assert(0);
1222 |
1223 | block.used = arr.length;
1224 | }
1225 |
1226 | /++
1227 | Marks the memory block associated with this array as unique, meaning
1228 | the runtime is allowed to free the old block immediately instead of
1229 | keeping it around for other lingering slices.
1230 |
1231 | In real D, the GC would take care of this but here I have to hack it.
1232 |
1233 | arsd.webasm extension
1234 | +/
1235 | void assumeUniqueReference(T)(T[] arr) {
1236 | auto block = getAllocatedBlock(arr.ptr);
1237 | if(block is null) assert(0);
1238 |
1239 | block.flags |= AllocatedBlock.Flags.unique;
1240 | }
1241 |
1242 | template _d_arraysetlengthTImpl(Tarr : T[], T) {
1243 | size_t _d_arraysetlengthT(return scope ref Tarr arr, size_t newlength) @trusted {
1244 | auto orig = arr;
1245 |
1246 | if(newlength <= arr.length) {
1247 | arr = arr[0 ..newlength];
1248 | } else {
1249 | auto ptr = cast(T*) realloc(cast(ubyte[])arr, newlength * T.sizeof);
1250 | arr = ptr[0 .. newlength];
1251 | if(orig !is null) {
1252 | arr[0 .. orig.length] = orig[];
1253 | }
1254 | }
1255 |
1256 | return newlength;
1257 | }
1258 | }
1259 |
1260 | extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) @trusted {
1261 | auto elemSize = ti.next.size;
1262 | auto newLength = n + px.length;
1263 | auto newSize = newLength * elemSize;
1264 | //import std.stdio; writeln(newSize, " ", newLength);
1265 | ubyte* ptr;
1266 | if(px.ptr is null)
1267 | ptr = malloc(newSize).ptr;
1268 | else // FIXME: anti-stomping by checking length == used
1269 | ptr = realloc(cast(ubyte[])px, newSize).ptr;
1270 | auto ns = ptr[0 .. newSize];
1271 | auto op = px.ptr;
1272 | auto ol = px.length * elemSize;
1273 |
1274 | foreach(i, b; op[0 .. ol])
1275 | ns[i] = b;
1276 |
1277 | (cast(size_t *)(&px))[0] = newLength;
1278 | (cast(void **)(&px))[1] = ns.ptr;
1279 | return px;
1280 | }
1281 |
1282 |
1283 | version(inline_concat)
1284 | extern(C) void[] _d_arraycatnTX(const TypeInfo ti, scope byte[][] arrs) @trusted
1285 | {
1286 | auto elemSize = ti.next.size;
1287 | size_t length;
1288 | foreach (b; arrs)
1289 | length += b.length;
1290 | if(!length)
1291 | return null;
1292 | ubyte* ptr = cast(ubyte*)malloc(length * elemSize);
1293 |
1294 | //Copy data
1295 | {
1296 | ubyte* nPtr = ptr;
1297 | foreach(b; arrs)
1298 | {
1299 | byte* bPtr = b.ptr;
1300 | size_t copySize = b.length*elemSize;
1301 | nPtr[0..copySize] = cast(ubyte[])bPtr[0..copySize];
1302 | nPtr+= copySize;
1303 | }
1304 | }
1305 | return cast(void[])ptr[0..length];
1306 | }
1307 |
1308 | version(inline_concat)
1309 | extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y)
1310 | {
1311 | import core.arsd.objectutils;
1312 | auto sizeelem = ti.next.size; // array element size
1313 | size_t xlen = x.length * sizeelem;
1314 | size_t ylen = y.length * sizeelem;
1315 | size_t len = xlen + ylen;
1316 |
1317 | if (!len)
1318 | return null;
1319 |
1320 | byte[] p = cast(byte[])malloc(len);
1321 | memcpy(p.ptr, x.ptr, xlen);
1322 | memcpy(p.ptr + xlen, y.ptr, ylen);
1323 | // do postblit processing
1324 | __doPostblit(p.ptr, xlen + ylen, ti.next);
1325 | return p[0 .. x.length + y.length];
1326 | }
1327 |
1328 | extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c)
1329 | {
1330 | // c could encode into from 1 to 4 characters
1331 | char[4] buf = void;
1332 | byte[] appendthis; // passed to appendT
1333 | if (c <= 0x7F)
1334 | {
1335 | buf.ptr[0] = cast(char)c;
1336 | appendthis = (cast(byte *)buf.ptr)[0..1];
1337 | }
1338 | else if (c <= 0x7FF)
1339 | {
1340 | buf.ptr[0] = cast(char)(0xC0 | (c >> 6));
1341 | buf.ptr[1] = cast(char)(0x80 | (c & 0x3F));
1342 | appendthis = (cast(byte *)buf.ptr)[0..2];
1343 | }
1344 | else if (c <= 0xFFFF)
1345 | {
1346 | buf.ptr[0] = cast(char)(0xE0 | (c >> 12));
1347 | buf.ptr[1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
1348 | buf.ptr[2] = cast(char)(0x80 | (c & 0x3F));
1349 | appendthis = (cast(byte *)buf.ptr)[0..3];
1350 | }
1351 | else if (c <= 0x10FFFF)
1352 | {
1353 | buf.ptr[0] = cast(char)(0xF0 | (c >> 18));
1354 | buf.ptr[1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
1355 | buf.ptr[2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
1356 | buf.ptr[3] = cast(char)(0x80 | (c & 0x3F));
1357 | appendthis = (cast(byte *)buf.ptr)[0..4];
1358 | }
1359 | else
1360 | assert(false, "Could not append dchar"); // invalid utf character - should we throw an exception instead?
1361 |
1362 | //
1363 | // TODO: This always assumes the array type is shared, because we do not
1364 | // get a typeinfo from the compiler. Assuming shared is the safest option.
1365 | // Once the compiler is fixed, the proper typeinfo should be forwarded.
1366 | //
1367 | return _d_arrayappendT(typeid(shared char[]), x, appendthis);
1368 | }
1369 |
1370 |
1371 |
1372 |
1373 | alias AliasSeq(T...) = T;
1374 | static foreach(type; AliasSeq!(byte, char, dchar, double, float, int, long, short, ubyte, uint, ulong, ushort, void, wchar)) {
1375 | mixin(q{
1376 | class TypeInfo_}~type.mangleof~q{ : TypeInfo {
1377 | override string toString() const pure nothrow @safe { return type.stringof; }
1378 | override size_t size() const { return type.sizeof; }
1379 | override @property size_t talign() const pure nothrow
1380 | {
1381 | return type.alignof;
1382 | }
1383 |
1384 | override bool equals(in void* a, in void* b) const {
1385 | static if(is(type == void))
1386 | return false;
1387 | else
1388 | return (*(cast(type*) a) == (*(cast(type*) b)));
1389 | }
1390 | static if(!is(type == void))
1391 | override size_t getHash(scope const void* p) @trusted const nothrow
1392 | {
1393 | return hashOf(*cast(const type *)p);
1394 | }
1395 | override const(void)[] initializer() pure nothrow @trusted const
1396 | {
1397 | static if(__traits(isZeroInit, type))
1398 | return (cast(void*)null)[0 .. type.sizeof];
1399 | else
1400 | {
1401 | static immutable type[1] c;
1402 | return c;
1403 | }
1404 | }
1405 | }
1406 | class TypeInfo_A}~type.mangleof~q{ : TypeInfo_Array {
1407 | override string toString() const { return (type[]).stringof; }
1408 | override const(TypeInfo) next() const { return cast(inout)typeid(type); }
1409 | override size_t getHash(scope const void* p) @trusted const nothrow
1410 | {
1411 | return hashOf(*cast(const type[]*) p);
1412 | }
1413 |
1414 | override bool equals(in void* av, in void* bv) const {
1415 | type[] a = *(cast(type[]*) av);
1416 | type[] b = *(cast(type[]*) bv);
1417 |
1418 | static if(is(type == void))
1419 | return false;
1420 | else {
1421 | foreach(idx, item; a)
1422 | if(item != b[idx])
1423 | return false;
1424 | return true;
1425 | }
1426 | }
1427 | }
1428 | });
1429 | }
1430 | // typeof(null)
1431 | class TypeInfo_n : TypeInfo
1432 | {
1433 | const: pure: @nogc: nothrow: @safe:
1434 | override string toString() { return "typeof(null)"; }
1435 | override size_t getHash(scope const void*) { return 0; }
1436 | override bool equals(in void*, in void*) { return true; }
1437 | override @property size_t size() { return typeof(null).sizeof; }
1438 | override const(void)[] initializer() @trusted { return (cast(void *)null)[0 .. size_t.sizeof]; }
1439 | }
1440 |
1441 | struct Interface {
1442 | TypeInfo_Class classinfo;
1443 | void*[] vtbl;
1444 | size_t offset;
1445 | }
1446 |
1447 | /**
1448 | * Array of pairs giving the offset and type information for each
1449 | * member in an aggregate.
1450 | */
1451 | struct OffsetTypeInfo
1452 | {
1453 | size_t offset; /// Offset of member from start of object
1454 | TypeInfo ti; /// TypeInfo for this member
1455 | }
1456 |
1457 | class TypeInfo_Axa : TypeInfo_Aa {
1458 |
1459 | }
1460 | class TypeInfo_Aya : TypeInfo_Aa {
1461 |
1462 | }
1463 |
1464 | class TypeInfo_Function : TypeInfo
1465 | {
1466 | override string toString() const pure @trusted{return deco;}
1467 | override bool opEquals(Object o)
1468 | {
1469 | if (this is o)
1470 | return true;
1471 | auto c = cast(const TypeInfo_Function)o;
1472 | return c && this.deco == c.deco;
1473 | }
1474 |
1475 | // BUG: need to add the rest of the functions
1476 |
1477 | override @property size_t size() nothrow pure const
1478 | {
1479 | return 0; // no size for functions
1480 | }
1481 | override const(void)[] initializer() const @safe{return null;}
1482 | TypeInfo _next;
1483 | override const(TypeInfo) next()nothrow pure inout @nogc { return _next; }
1484 |
1485 | /**
1486 | * Mangled function type string
1487 | */
1488 | string deco;
1489 | }
1490 |
1491 |
1492 | class TypeInfo_Delegate : TypeInfo {
1493 | TypeInfo next;
1494 | string deco;
1495 | override @property size_t size() nothrow pure const
1496 | {
1497 | alias dg = int delegate();
1498 | return dg.sizeof;
1499 | }
1500 | override bool equals(in void* p1, in void* p2) const
1501 | {
1502 | auto dg1 = *cast(void delegate()*)p1;
1503 | auto dg2 = *cast(void delegate()*)p2;
1504 | return dg1 == dg2;
1505 | }
1506 | override const(void)[] initializer() const @trusted
1507 | {
1508 | return (cast(void *)null)[0 .. (int delegate()).sizeof];
1509 | }
1510 | override size_t getHash(scope const void* p) @trusted const
1511 | {
1512 | return hashOf(*cast(const void delegate() *)p);
1513 | }
1514 |
1515 | override @property size_t talign() nothrow pure const
1516 | {
1517 | alias dg = int delegate();
1518 | return dg.alignof;
1519 | }
1520 | }
1521 |
1522 |
1523 | //Directly copied from LWDR source.
1524 | class TypeInfo_Interface : TypeInfo
1525 | {
1526 | TypeInfo_Class info;
1527 |
1528 | override bool equals(in void* p1, in void* p2) const
1529 | {
1530 | Interface* pi = **cast(Interface ***)*cast(void**)p1;
1531 | Object o1 = cast(Object)(*cast(void**)p1 - pi.offset);
1532 | pi = **cast(Interface ***)*cast(void**)p2;
1533 | Object o2 = cast(Object)(*cast(void**)p2 - pi.offset);
1534 |
1535 | return o1 == o2 || (o1 && o1.opCmp(o2) == 0);
1536 | }
1537 | override size_t getHash(scope const void* p) @trusted const
1538 | {
1539 | if (!*cast(void**)p)
1540 | {
1541 | return 0;
1542 | }
1543 | Interface* pi = **cast(Interface ***)*cast(void**)p;
1544 | Object o = cast(Object)(*cast(void**)p - pi.offset);
1545 | assert(o);
1546 | return o.toHash();
1547 | }
1548 |
1549 | override const(void)[] initializer() const @trusted
1550 | {
1551 | return (cast(void *)null)[0 .. Object.sizeof];
1552 | }
1553 |
1554 | override @property size_t size() nothrow pure const
1555 | {
1556 | return Object.sizeof;
1557 | }
1558 | }
1559 |
1560 | class TypeInfo_Const : TypeInfo {
1561 | override size_t getHash(scope const(void*) p) @trusted const nothrow { return base.getHash(p); }
1562 | TypeInfo base;
1563 | override size_t size() const { return base.size; }
1564 | override const(TypeInfo) next() const { return base.next; }
1565 | override const(void)[] initializer() nothrow pure const{return base.initializer();}
1566 | override @property size_t talign() nothrow pure const { return base.talign; }
1567 | override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); }
1568 | }
1569 |
1570 |
1571 | ///For some reason, getHash for interfaces wanted that
1572 | pragma(mangle, "_D9invariant12_d_invariantFC6ObjectZv")
1573 | extern(D) void _d_invariant(Object o)
1574 | {
1575 | TypeInfo_Class c;
1576 |
1577 | //printf("__d_invariant(%p)\n", o);
1578 |
1579 | // BUG: needs to be filename/line of caller, not library routine
1580 | assert(o !is null); // just do null check, not invariant check
1581 |
1582 | c = typeid(o);
1583 | do
1584 | {
1585 | if (c.classInvariant)
1586 | {
1587 | (*c.classInvariant)(o);
1588 | }
1589 | c = c.base;
1590 | } while (c);
1591 | }
1592 |
1593 | /+
1594 | class TypeInfo_Immutable : TypeInfo {
1595 | size_t getHash(in void*) nothrow { return 0; }
1596 | TypeInfo base;
1597 | }
1598 | +/
1599 | class TypeInfo_Invariant : TypeInfo {
1600 | TypeInfo base;
1601 | override size_t getHash(scope const (void*) p) @trusted const nothrow { return base.getHash(p); }
1602 | override size_t size() const { return base.size; }
1603 | override const(TypeInfo) next() const { return base; }
1604 | }
1605 | class TypeInfo_Shared : TypeInfo {
1606 | override size_t getHash(scope const (void*) p) @trusted const nothrow { return base.getHash(p); }
1607 | TypeInfo base;
1608 | override size_t size() const { return base.size; }
1609 | override const(TypeInfo) next() const { return base; }
1610 | }
1611 | class TypeInfo_Inout : TypeInfo {
1612 | override size_t getHash(scope const (void*) p) @trusted const nothrow { return base.getHash(p); }
1613 | TypeInfo base;
1614 | override size_t size() const { return base.size; }
1615 | override const(TypeInfo) next() const { return base; }
1616 | }
1617 |
1618 | class TypeInfo_Struct : TypeInfo {
1619 | string name;
1620 | void[] m_init;
1621 | @safe pure nothrow
1622 | {
1623 | size_t function(in void*) xtoHash;
1624 | bool function(in void*, in void*) xopEquals;
1625 | int function(in void*, in void*) xopCmp;
1626 | string function(in void*) xtoString;
1627 | }
1628 | uint m_flags;
1629 | union {
1630 | void function(void*) xdtor;
1631 | void function(void*, const TypeInfo_Struct) xdtorti;
1632 | }
1633 | void function(void*) xpostblit;
1634 | uint align_;
1635 | immutable(void)* rtinfo;
1636 | // private struct _memberFunc //? Is it necessary
1637 | // {
1638 | // union
1639 | // {
1640 | // struct // delegate
1641 | // {
1642 | // const void* ptr;
1643 | // const void* funcptr;
1644 | // }
1645 | // @safe pure nothrow
1646 | // {
1647 | // bool delegate(in void*) xopEquals;
1648 | // int delegate(in void*) xopCmp;
1649 | // }
1650 | // }
1651 | // }
1652 |
1653 | enum StructFlags : uint
1654 | {
1655 | hasPointers = 0x1,
1656 | isDynamicType = 0x2, // built at runtime, needs type info in xdtor
1657 | }
1658 | override size_t size() const { return m_init.length; }
1659 | override @property uint flags() nothrow pure const @safe @nogc { return m_flags; }
1660 |
1661 | override size_t toHash() const
1662 | {
1663 | return hashOf(this.name);
1664 | }
1665 | override bool opEquals(Object o)
1666 | {
1667 | if (this is o)
1668 | return true;
1669 | auto s = cast(const TypeInfo_Struct)o;
1670 | return s && this.name == s.name;
1671 | }
1672 | override size_t getHash(scope const void* p) @trusted pure nothrow const
1673 | {
1674 | assert(p);
1675 | if (xtoHash)
1676 | {
1677 | return (*xtoHash)(p);
1678 | }
1679 | else
1680 | {
1681 | return hashOf(p[0 .. initializer().length]);
1682 | }
1683 | }
1684 |
1685 |
1686 | override bool equals(in void* p1, in void* p2) @trusted const
1687 | {
1688 | if (!p1 || !p2)
1689 | return false;
1690 | else if (xopEquals)
1691 | return (*xopEquals)(p1, p2);
1692 | else if (p1 == p2)
1693 | return true;
1694 | else
1695 | // BUG: relies on the GC not moving objects
1696 | return memcmp(p1, p2, m_init.length) == 0;
1697 | }
1698 | override @property size_t talign() nothrow pure const { return align_; }
1699 | final override void destroy(void* p) const
1700 | {
1701 | if (xdtor)
1702 | {
1703 | if (m_flags & StructFlags.isDynamicType)
1704 | (*xdtorti)(p, this);
1705 | else
1706 | (*xdtor)(p);
1707 | }
1708 | }
1709 |
1710 | override void postblit(void* p) const
1711 | {
1712 | if (xpostblit)
1713 | (*xpostblit)(p);
1714 | }
1715 |
1716 | override const(void)[] initializer() nothrow pure const @safe
1717 | {
1718 | return m_init;
1719 | }
1720 |
1721 | }
1722 |
1723 | extern(C) bool _xopCmp(in void*, in void*) { return false; }
1724 |
1725 | // }
1726 |
1727 | void __ArrayDtor(T)(scope T[] a)
1728 | {
1729 | foreach_reverse (ref T e; a)
1730 | e.__xdtor();
1731 | }
1732 |
1733 | TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) nothrow
1734 | {
1735 | const fromSize = from.length * TFrom.sizeof;
1736 | const toLength = fromSize / TTo.sizeof;
1737 |
1738 | if ((fromSize % TTo.sizeof) != 0)
1739 | {
1740 | //onArrayCastError(TFrom.stringof, fromSize, TTo.stringof, toLength * TTo.sizeof);
1741 | import arsd.webassembly;
1742 | abort();
1743 | }
1744 |
1745 | struct Array
1746 | {
1747 | size_t length;
1748 | void* ptr;
1749 | }
1750 | auto a = cast(Array*)&from;
1751 | a.length = toLength; // jam new length
1752 | return *cast(TTo[]*)a;
1753 | }
1754 |
1755 | extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y)
1756 | {
1757 | auto length = x.length;
1758 | auto tinext = ti.next;
1759 | auto sizeelem = tinext./*t*/size; // array element size
1760 | _d_arrayappendcTX(ti, x, y.length);
1761 | memcpy(x.ptr + length * sizeelem, y.ptr, y.length * sizeelem);
1762 |
1763 | // do postblit
1764 | //__doPostblit(x.ptr + length * sizeelem, y.length * sizeelem, tinext);
1765 | return x;
1766 | }
1767 |
1768 | extern (C) int _adEq2(void[] a1, void[] a2, TypeInfo ti)
1769 | {
1770 | debug(adi) printf("_adEq2(a1.length = %d, a2.length = %d)\n", a1.length, a2. length);
1771 | if (a1.length != a2.length)
1772 | return 0; // not equal
1773 | if (!ti.equals(&a1, &a2))
1774 | return 0;
1775 | return 1;
1776 | }
1777 |
1778 | V[K] dup(T : V[K], K, V)(T aa)
1779 | {
1780 | //pragma(msg, "K = ", K, ", V = ", V);
1781 |
1782 | // Bug10720 - check whether V is copyable
1783 | static assert(is(typeof({ V v = aa[K.init]; })),
1784 | "cannot call " ~ T.stringof ~ ".dup because " ~ V.stringof ~ " is not copyable");
1785 |
1786 | V[K] result;
1787 |
1788 | //foreach (k, ref v; aa)
1789 | // result[k] = v; // Bug13701 - won't work if V is not mutable
1790 |
1791 | ref V duplicateElem(ref K k, ref const V v) @trusted pure nothrow
1792 | {
1793 | void* pv = _aaGetY(cast(AA*)&result, typeid(V[K]), V.sizeof, &k);
1794 | memcpy(pv, &v, V.sizeof);
1795 | return *cast(V*)pv;
1796 | }
1797 |
1798 | foreach (k, ref v; aa)
1799 | {
1800 | static if (!__traits(hasPostblit, V))
1801 | duplicateElem(k, v);
1802 | else static if (__traits(isStaticArray, V))
1803 | _doPostblit(duplicateElem(k, v)[]);
1804 | else static if (!is(typeof(v.__xpostblit())) && is(immutable V == immutable UV, UV))
1805 | (() @trusted => *cast(UV*) &duplicateElem(k, v))().__xpostblit();
1806 | else
1807 | duplicateElem(k, v).__xpostblit();
1808 | }
1809 |
1810 | return result;
1811 | }
1812 |
1813 | /** ditto */
1814 | V[K] dup(T : V[K], K, V)(T* aa)
1815 | {
1816 | return (*aa).dup;
1817 | }
1818 |
1819 | T[] dup(T)(scope T[] array) pure nothrow @trusted if (__traits(isPOD, T) && !is(const(T) : T))
1820 | {
1821 | T[] result;
1822 | foreach(ref e; array) {
1823 | result ~= e;
1824 | }
1825 | return result;
1826 | }
1827 |
1828 |
1829 |
1830 | T[] dup(T)(scope const(T)[] array) pure nothrow @trusted if (__traits(isPOD, T))
1831 | {
1832 | T[] result;
1833 | foreach(ref e; array) {
1834 | result ~= e;
1835 | }
1836 | return result;
1837 | }
1838 |
1839 | immutable(T)[] idup(T)(scope const(T)[] array) pure nothrow @trusted
1840 | {
1841 | immutable(T)[] result;
1842 | foreach(ref e; array) {
1843 | result ~= e;
1844 | }
1845 | return result;
1846 | }
1847 |
1848 | class Error { this(string msg) {} }
1849 | class Throwable : Object
1850 | {
1851 | interface TraceInfo
1852 | {
1853 | int opApply(scope int delegate(ref const(char[]))) const;
1854 | int opApply(scope int delegate(ref size_t, ref const(char[]))) const;
1855 | string toString() const;
1856 | }
1857 |
1858 | string msg; /// A message describing the error.
1859 |
1860 | /**
1861 | * The _file name of the D source code corresponding with
1862 | * where the error was thrown from.
1863 | */
1864 | string file;
1865 | /**
1866 | * The _line number of the D source code corresponding with
1867 | * where the error was thrown from.
1868 | */
1869 | size_t line;
1870 |
1871 | /**
1872 | * The stack trace of where the error happened. This is an opaque object
1873 | * that can either be converted to $(D string), or iterated over with $(D
1874 | * foreach) to extract the items in the stack trace (as strings).
1875 | */
1876 | TraceInfo info;
1877 |
1878 | /**
1879 | * A reference to the _next error in the list. This is used when a new
1880 | * $(D Throwable) is thrown from inside a $(D catch) block. The originally
1881 | * caught $(D Exception) will be chained to the new $(D Throwable) via this
1882 | * field.
1883 | */
1884 | private Throwable nextInChain;
1885 |
1886 | private uint _refcount; // 0 : allocated by GC
1887 | // 1 : allocated by _d_newThrowable()
1888 | // 2.. : reference count + 1
1889 |
1890 | /**
1891 | * Returns:
1892 | * A reference to the _next error in the list. This is used when a new
1893 | * $(D Throwable) is thrown from inside a $(D catch) block. The originally
1894 | * caught $(D Exception) will be chained to the new $(D Throwable) via this
1895 | * field.
1896 | */
1897 | @property inout(Throwable) next() @safe inout return scope pure nothrow @nogc { return nextInChain; }
1898 |
1899 | /**
1900 | * Replace next in chain with `tail`.
1901 | * Use `chainTogether` instead if at all possible.
1902 | */
1903 | @property void next(Throwable tail) @safe scope pure nothrow @nogc{}
1904 |
1905 | /**
1906 | * Returns:
1907 | * mutable reference to the reference count, which is
1908 | * 0 - allocated by the GC, 1 - allocated by _d_newThrowable(),
1909 | * and >=2 which is the reference count + 1
1910 | * Note:
1911 | * Marked as `@system` to discourage casual use of it.
1912 | */
1913 | @system @nogc final pure nothrow ref uint refcount() return { return _refcount; }
1914 |
1915 | /**
1916 | * Loop over the chain of Throwables.
1917 | */
1918 | int opApply(scope int delegate(Throwable) dg)
1919 | {
1920 | int result = 0;
1921 | for (Throwable t = this; t; t = t.nextInChain)
1922 | {
1923 | result = dg(t);
1924 | if (result)
1925 | break;
1926 | }
1927 | return result;
1928 | }
1929 |
1930 | /**
1931 | * Append `e2` to chain of exceptions that starts with `e1`.
1932 | * Params:
1933 | * e1 = start of chain (can be null)
1934 | * e2 = second part of chain (can be null)
1935 | * Returns:
1936 | * Throwable that is at the start of the chain; null if both `e1` and `e2` are null
1937 | */
1938 | static @system @nogc pure nothrow Throwable chainTogether(return scope Throwable e1, return scope Throwable e2)
1939 | {
1940 | if (!e1)
1941 | return e2;
1942 | if (!e2)
1943 | return e1;
1944 | if (e2.refcount())
1945 | ++e2.refcount();
1946 |
1947 | for (auto e = e1; 1; e = e.nextInChain)
1948 | {
1949 | if (!e.nextInChain)
1950 | {
1951 | e.nextInChain = e2;
1952 | break;
1953 | }
1954 | }
1955 | return e1;
1956 | }
1957 |
1958 | @nogc @safe pure nothrow this(string msg, Throwable nextInChain = null)
1959 | {
1960 | this.msg = msg;
1961 | this.nextInChain = nextInChain;
1962 | if (nextInChain && nextInChain._refcount)
1963 | ++nextInChain._refcount;
1964 | //this.info = _d_traceContext();
1965 | }
1966 |
1967 | @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null)
1968 | {
1969 | this(msg, nextInChain);
1970 | this.file = file;
1971 | this.line = line;
1972 | //this.info = _d_traceContext();
1973 | }
1974 |
1975 | @trusted nothrow ~this(){}
1976 |
1977 | /**
1978 | * Overrides $(D Object.toString) and returns the error message.
1979 | * Internally this forwards to the $(D toString) overload that
1980 | * takes a $(D_PARAM sink) delegate.
1981 | */
1982 | override string toString()
1983 | {
1984 | string s;
1985 | toString((in buf) { s ~= buf; });
1986 | return s;
1987 | }
1988 |
1989 | /**
1990 | * The Throwable hierarchy uses a toString overload that takes a
1991 | * $(D_PARAM _sink) delegate to avoid GC allocations, which cannot be
1992 | * performed in certain error situations. Override this $(D
1993 | * toString) method to customize the error message.
1994 | */
1995 | void toString(scope void delegate(in char[]) sink) const{}
1996 |
1997 | /**
1998 | * Get the message describing the error.
1999 | * Base behavior is to return the `Throwable.msg` field.
2000 | * Override to return some other error message.
2001 | *
2002 | * Returns:
2003 | * Error message
2004 | */
2005 | const(char)[] message() const
2006 | {
2007 | return this.msg;
2008 | }
2009 | }
2010 | class Exception : Throwable
2011 | {
2012 |
2013 | /**
2014 | * Creates a new instance of Exception. The nextInChain parameter is used
2015 | * internally and should always be $(D null) when passed by user code.
2016 | * This constructor does not automatically throw the newly-created
2017 | * Exception; the $(D throw) statement should be used for that purpose.
2018 | */
2019 | @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
2020 | {
2021 | super(msg, file, line, nextInChain);
2022 | }
2023 |
2024 | @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
2025 | {
2026 | super(msg, file, line, nextInChain);
2027 | }
2028 | }
2029 |
2030 |
2031 | import core.internal.hash;
2032 |
--------------------------------------------------------------------------------
/arsd-webassembly/std/random.d:
--------------------------------------------------------------------------------
1 | module std.random;
2 |
3 | import arsd.webassembly;
4 |
5 | int uniform(int low, int high) {
6 | int max = high - low;
7 | return low + eval!int(q{ return Math.floor(Math.random() * $0); }, max);
8 | }
9 |
--------------------------------------------------------------------------------
/arsd-webassembly/std/stdio.d:
--------------------------------------------------------------------------------
1 | module std.stdio;
2 |
3 | import arsd.webassembly;
4 |
5 | void writeln(T...)(T t) {
6 | eval(q{
7 | var str = "";
8 | for(var i = 0; i < arguments.length; i++)
9 | str += arguments[i];
10 |
11 | str += "\n";
12 |
13 | var txt = document.createTextNode(str);
14 | var fd = document.getElementById("stdout");
15 | fd.appendChild(txt);
16 | }, t);
17 | }
--------------------------------------------------------------------------------
/build.bat:
--------------------------------------------------------------------------------
1 | cmd /c "ldc2 -i=. --d-version=CarelessAlocation -i=std -Iarsd-webassembly/ -L-allow-undefined -ofserver/omg.wasm -mtriple=wasm32-unknown-unknown-wasm arsd-webassembly/core/arsd/aa arsd-webassembly/core/arsd/objectutils arsd-webassembly/core/internal/utf arsd-webassembly/core/arsd/utf_decoding hello arsd-webassembly/object.d"
--------------------------------------------------------------------------------
/fail.d:
--------------------------------------------------------------------------------
1 | just_seeing_compile_error_in_browser
2 |
--------------------------------------------------------------------------------
/hello.d:
--------------------------------------------------------------------------------
1 | // ldc2 -i=. -i=std -Iarsd-webassembly/ -L-allow-undefined -ofserver/omg.wasm -mtriple=wasm32-unknown-unknown-wasm omg arsd-webassembly/object.d
2 |
3 | import arsd.webassembly;
4 |
5 | class A {
6 | int a() { return 123; }
7 | }
8 |
9 | class B : A {
10 | int val;
11 | override int a() { return 455 + val; }
12 | }
13 |
14 | import std.stdio;
15 |
16 | void main() {
17 | B b = new B;
18 | b.val = 5;
19 | A a = b;
20 |
21 | int num = eval!int(q{ console.log("hi " + $1 + ", " + $0); this.omg = "yay"; return 52; }, a.a(), "hello world");
22 | eval(q{ console.log("asdasd " + this.omg + " " + $0); }, num);
23 |
24 |
25 | NativeHandle body = eval!NativeHandle("return document.body");
26 | body.methods.insertAdjacentHTML!void("beforeend", "hello world");
27 | eval(`console.log($0)`, body.properties.innerHTML!string);
28 |
29 | writeln("writeln!!!");
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/server/Makefile:
--------------------------------------------------------------------------------
1 | # This assumes you have my dmdi script but basically you just bring down the arsd lib into here then dmd -i and you get the same result.
2 | all:
3 | dmdi serve.d -version=embedded_httpd
4 |
--------------------------------------------------------------------------------
/server/serve.d:
--------------------------------------------------------------------------------
1 | import arsd.cgi;
2 |
3 | import std.file;
4 | import std.string;
5 | import std.process;
6 |
7 | // -Wl,--export=__heap_base
8 |
9 | // https://github.com/skoppe/wasm-sourcemaps
10 |
11 | void handler(Cgi cgi) {
12 |
13 | if(cgi.pathInfo == "/webassembly-core.js") {
14 | cgi.setResponseContentType("text/javascript");
15 | cgi.write(readText("webassembly-core.js"), true);
16 | return;
17 | }
18 |
19 |
20 | // lol trivial validation
21 | size_t dot;
22 | {
23 | if(cgi.pathInfo.length > 32) return;
24 | foreach(idx, ch; cgi.pathInfo[1 .. $]) {
25 | if(!(
26 | (ch >= '0' && ch <= '9') ||
27 | (ch >= 'A' && ch <= 'Z') ||
28 | (ch >= 'a' && ch <= 'z') ||
29 | ch == '_' ||
30 | ch == '.'
31 | )) return;
32 | if(ch == '.') {
33 | if(idx == 0) return;
34 | if(dot) return;
35 | dot = idx;
36 | }
37 | }
38 | }
39 |
40 | auto path = cgi.pathInfo.length == 0 ? "" : cgi.pathInfo[1 .. $];
41 |
42 | if(dot && path[dot .. $] == ".wasm") {
43 | cgi.setResponseContentType("application/wasm");
44 | cgi.write(read("../" ~ path), true);
45 | return;
46 |
47 | }
48 |
49 | if(path.length == 0) {
50 | // index
51 | string html = "
Rules:
Please note: this code doesn't work on Safari.
16 |Also try the arrow keys and spacebar to interact with many of these programs. Tip: shift+right click can get the normal menu on the canvas.
17 | 18 | View Source 19 | 20 | 21 | 22 | 23 | 24 | 25 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /test_runtime.d: -------------------------------------------------------------------------------- 1 | // ldc2 -i=. --d-version=CarelessAlocation -i=std -Iarsd-webassembly/ -L-allow-undefined -ofserver/omg.wasm -mtriple=wasm32-unknown-unknown-wasm arsd-webassembly/core/arsd/aa arsd-webassembly/core/arsd/objectutils arsd-webassembly/core/internal/utf arsd-webassembly/core/arsd/utf_decoding hello arsd-webassembly/object.d 2 | 3 | import arsd.webassembly; 4 | import std.stdio; 5 | 6 | alias thisModule = __traits(parent, {}); 7 | 8 | class A { 9 | int _b = 200; 10 | int a() { return 123; } 11 | } 12 | 13 | interface C { 14 | void test(); 15 | } 16 | interface D { 17 | void check(); 18 | } 19 | 20 | class B : A, C 21 | { 22 | int val; 23 | override int a() { return 455 + val; } 24 | 25 | void test() 26 | { 27 | rawlog(a()); 28 | int[] a; 29 | a~= 1; 30 | } 31 | } 32 | 33 | void rawlog(Args...)(Args a, string file = __FILE__, size_t line = __LINE__) 34 | { 35 | writeln(a, " at "~ file~ ":", line); 36 | } 37 | 38 | 39 | struct Tester 40 | { 41 | int b = 50; 42 | string a = "hello"; 43 | } 44 | void main() 45 | { 46 | float[] f = new float[4]; 47 | assert(f[0] is float.init); 48 | f~= 5.5; //Append 49 | f~= [3, 4]; 50 | int[] inlineConcatTest = [1, 2] ~ [3, 4]; 51 | 52 | auto dg = delegate() 53 | { 54 | writeln(inlineConcatTest[0], f[1]); 55 | }; 56 | dg(); 57 | B b = new B; 58 | b.val = 5; 59 | A a = b; 60 | a.a(); 61 | C c = b; 62 | c.test(); 63 | assert(cast(D)c is null); 64 | Tester[] t = new Tester[10]; 65 | assert(t[0] == Tester.init); 66 | assert(t.length == 10); 67 | 68 | switch("hello") 69 | { 70 | case "test": 71 | writeln("broken"); 72 | break; 73 | case "hello": 74 | writeln("Working switch string"); 75 | break; 76 | default: writeln("What happenned here?"); 77 | } 78 | string strTest = "test"[0..$]; 79 | assert(strTest == "test"); 80 | 81 | 82 | Tester* structObj = new Tester(50_000, "Inline Allocation"); 83 | writeln(structObj is null, structObj.a, structObj.b); 84 | 85 | int[string] hello = ["hello": 500]; 86 | assert(("hello" in hello) !is null, "No key hello yet..."); 87 | assert(hello["hello"] == 500, "Not 500"); 88 | hello["hello"] = 1200; 89 | assert(hello["hello"] == 1200, "Reassign didn't work"); 90 | hello["h2o"] = 250; 91 | assert(hello["h2o"] == 250, "New member"); 92 | 93 | 94 | int[] appendTest; 95 | appendTest~= 50; 96 | appendTest~= 500; 97 | appendTest~= 5000; 98 | foreach(v; appendTest) 99 | writeln(v); 100 | string strConcatTest; 101 | strConcatTest~= "Hello"; 102 | strConcatTest~= "World"; 103 | writeln(strConcatTest); 104 | int[] intConcatTest = cast(int[2])[1, 2]; 105 | intConcatTest~= 50; 106 | string decInput = "a"; 107 | decInput~= "こんいちは"; 108 | foreach(dchar ch; "こんいちは") 109 | { 110 | decInput~= ch; 111 | writeln(ch); 112 | } 113 | writeln(decInput); 114 | int[] arrCastTest = [int.max]; 115 | 116 | foreach(v; cast(ubyte[])arrCastTest) 117 | writeln(v); 118 | 119 | 120 | 121 | enum Type 122 | { 123 | int_, 124 | string_, 125 | } 126 | struct TestWithPtr 127 | { 128 | int* a; 129 | Type t = Type.string_; 130 | } 131 | 132 | TestWithPtr[] _; 133 | _~= TestWithPtr(new int(50), Type.int_); 134 | _ = _[0..$-1]; 135 | _~= TestWithPtr(new int(100), Type.string_); 136 | _~= TestWithPtr(new int(150), Type.string_); 137 | _~= TestWithPtr(new int(200), Type.int_); 138 | 139 | foreach(v; _) 140 | writeln(*v.a); 141 | 142 | 143 | char[] sup; 144 | string rev; 145 | 146 | // string test = null; 147 | for(int i = 'a'; i <= 'z'; i++) 148 | { 149 | sup~= cast(char)i; 150 | rev~= ('z' - cast(char)i) + 'a'; 151 | } 152 | writeln((typeid(sup)).toString); 153 | 154 | static foreach(mem; __traits(allMembers, std.stdio)) 155 | writeln(mem); 156 | 157 | float[][] matrixTest = new float[][](8, 8); 158 | 159 | foreach(array; matrixTest) 160 | foreach(value; array) 161 | writeln(value); 162 | 163 | // foreach(array; matrixTest) 164 | // foreach(value; array) 165 | // writeln(value); 166 | 167 | 168 | assert(false, sup~sup~sup); 169 | } -------------------------------------------------------------------------------- /tetris.d: -------------------------------------------------------------------------------- 1 | import arsd.simpledisplay; 2 | import arsd.simpleaudio; 3 | 4 | enum PieceSize = 16; 5 | 6 | enum SettleStatus { 7 | none, 8 | settled, 9 | cleared, 10 | tetris, 11 | gameOver, 12 | } 13 | 14 | class Board { 15 | int width; 16 | int height; 17 | int[] state; 18 | int score; 19 | this(int width, int height) { 20 | state = new int[](width * height); 21 | this.width = width; 22 | this.height = height; 23 | } 24 | 25 | SettleStatus settlePiece(Piece piece) { 26 | if(piece.y <= 0) 27 | return SettleStatus.gameOver; 28 | 29 | SettleStatus status = SettleStatus.settled; 30 | foreach(yo, line; pieces[piece.type][piece.rotation]) { 31 | int mline = line; 32 | foreach(xo; 0 .. 4) { 33 | if(mline & 0b1000) 34 | state[(piece.y+yo) * width + xo + piece.x] = piece.type + 1; 35 | mline <<= 1; 36 | } 37 | } 38 | 39 | int[4] del; 40 | int delPos = 0; 41 | 42 | foreach(y; piece.y .. piece.y + 4) { 43 | int presentCount; 44 | if(y >= height) 45 | break; 46 | foreach(x; 0 .. width) 47 | if(state[y * width + x]) 48 | presentCount++; 49 | 50 | if(presentCount == width) { 51 | del[delPos++] = y; 52 | status = SettleStatus.cleared; 53 | } 54 | } 55 | 56 | if(delPos == 4) { 57 | score += 4; // tetris bonus! 58 | status = SettleStatus.tetris; 59 | } 60 | 61 | foreach(p; 0 .. delPos) { 62 | foreach_reverse(y; 0 .. del[p]) 63 | state[(y + 1) * width .. (y + 2) * width] = state[(y + 0) * width .. (y + 1) * width]; 64 | state[0 .. width] = 0; 65 | 66 | score++; 67 | } 68 | 69 | return status; 70 | 71 | /+ 72 | import std.stdio; 73 | writeln; 74 | writeln; 75 | foreach(y; 0 .. height) { 76 | foreach(x; 0 .. width) { 77 | write(state[y * width + x]); 78 | } 79 | writeln(""); 80 | } 81 | +/ 82 | } 83 | 84 | SettleStatus trySettle(Piece piece) { 85 | auto pieceMap = pieces[piece.type][piece.rotation]; 86 | int ph = 4; 87 | foreach_reverse(line; pieceMap) { 88 | if(line) 89 | break; 90 | ph--; 91 | } 92 | if(ph + piece.y >= this.height) { 93 | if(!piece.settleNextFrame) { 94 | piece.settleNextFrame = true; 95 | return SettleStatus.none; 96 | } else { 97 | return settlePiece(piece); 98 | } 99 | } 100 | piece.y++; 101 | if(collisionDetect(piece)) { 102 | piece.y--; 103 | 104 | if(!piece.settleNextFrame) { 105 | piece.settleNextFrame = true; 106 | return SettleStatus.none; 107 | } else { 108 | return settlePiece(piece); 109 | } 110 | } else { 111 | piece.settleNextFrame = false; 112 | } 113 | piece.y--; 114 | return SettleStatus.none; 115 | } 116 | 117 | bool collisionDetect(Piece piece) { 118 | auto pieceMap = pieces[piece.type][piece.rotation]; 119 | foreach_reverse(yo,line; pieceMap) { 120 | int mline = line; 121 | foreach(xo; 0 .. 4) { 122 | if(mline & 0b1000) { 123 | // FIXME: potential range violation 124 | if(state[(piece.y+yo) * this.width + xo + piece.x]) 125 | return true; 126 | } 127 | mline <<= 1; 128 | } 129 | } 130 | return false; 131 | } 132 | 133 | void redraw(SimpleWindow window) { 134 | auto painter = window.draw(); 135 | int x, y; 136 | foreach(s; state) { 137 | painter.fillColor = s ? palette[s - 1] : Color.black; 138 | painter.outlineColor = s ? Color.white : Color.black; 139 | painter.drawRectangle(Point(x, y) * PieceSize, PieceSize, PieceSize); 140 | x++; 141 | if(x == width) { 142 | x = 0; 143 | y++; 144 | } 145 | } 146 | } 147 | } 148 | 149 | static immutable ubyte[][][] pieces = [ 150 | // long straight 151 | [[0b1000, 152 | 0b1000, 153 | 0b1000, 154 | 0b1000], 155 | [0b1111, 156 | 0b0000, 157 | 0b0000, 158 | 0b0000]], 159 | // l 160 | [[0b1000, 161 | 0b1000, 162 | 0b1100, 163 | 0b0000], 164 | [0b0010, 165 | 0b1110, 166 | 0b0000, 167 | 0b0000], 168 | [0b1100, 169 | 0b0100, 170 | 0b0100, 171 | 0b0000], 172 | [0b1110, 173 | 0b1000, 174 | 0b0000, 175 | 0b0000]], 176 | // j 177 | [[0b0100, 178 | 0b0100, 179 | 0b1100, 180 | 0b0000], 181 | [0b1000, 182 | 0b1110, 183 | 0b0000, 184 | 0b0000], 185 | [0b1100, 186 | 0b1000, 187 | 0b1000, 188 | 0b0000], 189 | [0b1110, 190 | 0b0010, 191 | 0b0000, 192 | 0b0000]], 193 | // n 194 | [[0b1100, 195 | 0b0110, 196 | 0b0000, 197 | 0b0000], 198 | [0b0100, 199 | 0b1100, 200 | 0b1000, 201 | 0b0000]], 202 | // other n 203 | [[0b0110, 204 | 0b1100, 205 | 0b0000, 206 | 0b0000], 207 | [0b1000, 208 | 0b1100, 209 | 0b0100, 210 | 0b0000]], 211 | // t 212 | [[0b0100, 213 | 0b1110, 214 | 0b0000, 215 | 0b0000], 216 | [0b1000, 217 | 0b1100, 218 | 0b1000, 219 | 0b0000], 220 | [0b1110, 221 | 0b0100, 222 | 0b0000, 223 | 0b0000], 224 | [0b0100, 225 | 0b1100, 226 | 0b0100, 227 | 0b0000]], 228 | // square 229 | [[0b1100, 230 | 0b1100, 231 | 0b0000, 232 | 0b0000]], 233 | ]; 234 | 235 | immutable Color[] palette = [ 236 | Color.red, 237 | Color.blue, 238 | Color.green, 239 | Color.yellow, 240 | Color.teal, 241 | Color.purple, 242 | Color.gray 243 | ]; 244 | 245 | static assert(palette.length == pieces.length); 246 | 247 | class Piece { 248 | SimpleWindow window; 249 | Board board; 250 | this(SimpleWindow window, Board board) { 251 | this.window = window; 252 | this.board = board; 253 | } 254 | 255 | static int randomType() { 256 | import std.random; 257 | return uniform(0, cast(int) pieces.length); 258 | } 259 | 260 | int width() { 261 | int fw = 0; 262 | foreach(int s; pieces[type][rotation]) { 263 | int w = 4; 264 | while(w && ((s & 1) == 0)) { 265 | w--; 266 | s >>= 1; 267 | } 268 | if(w > fw) 269 | fw = w; 270 | } 271 | return fw; 272 | } 273 | 274 | void reset(int type) { 275 | this.type = type; 276 | rotation = 0; 277 | x = board.width / 2 - 1; 278 | y = 0; 279 | settleNextFrame = false; 280 | } 281 | 282 | int type; 283 | int rotation; 284 | 285 | int x; 286 | int y; 287 | 288 | bool settleNextFrame; 289 | 290 | void erase() { 291 | draw(true); 292 | } 293 | 294 | void draw(bool erase = false) { 295 | auto painter = window.draw(); 296 | painter.fillColor = erase ? Color.black : palette[type]; 297 | painter.outlineColor = erase ? Color.black : Color.white; 298 | foreach(yo, line; pieces[type][rotation]) { 299 | int mline = line; 300 | foreach(xo; 0 .. 4) { 301 | if(mline & 0b1000) 302 | painter.drawRectangle(Point(cast(int) (x + xo), cast(int) (y + yo)) * PieceSize, PieceSize, PieceSize); 303 | mline <<= 1; 304 | } 305 | } 306 | } 307 | 308 | void moveDown() { 309 | if(!settleNextFrame) { 310 | y++; 311 | if(board.collisionDetect(this)) 312 | y--; 313 | } 314 | } 315 | 316 | void moveLeft() { 317 | if(x) { 318 | x--; 319 | if(board.collisionDetect(this)) 320 | x++; 321 | } 322 | } 323 | 324 | void moveRight() { 325 | if(x + width < board.width) { 326 | x++; 327 | if(board.collisionDetect(this)) 328 | x--; 329 | } 330 | } 331 | 332 | void rotate() { 333 | rotation++; 334 | if(rotation >= pieces[type].length) 335 | rotation = 0; 336 | if(x + width > board.width) 337 | x = board.width - width; 338 | } 339 | } 340 | 341 | void main() { 342 | auto audio = AudioOutputThread(0); 343 | //audio.start(); 344 | 345 | auto board = new Board(10, 20); 346 | 347 | auto window = new SimpleWindow(board.width * PieceSize, board.height * PieceSize, "Detris"); 348 | 349 | // clear screen to black 350 | { 351 | auto painter = window.draw(); 352 | painter.outlineColor = Color.black; 353 | painter.fillColor = Color.black; 354 | painter.drawRectangle(Point(0, 0), Size(window.width, window.height)); 355 | } 356 | 357 | Piece currentPiece = new Piece(window, board); 358 | 359 | int frameCounter; 360 | bool downPressed; 361 | 362 | int gameOverY = 0; 363 | 364 | int difficulty = 1; 365 | 366 | window.eventLoop(100 / 5, () { 367 | 368 | if(gameOverY > board.height + 1) { 369 | window.close(); 370 | return; 371 | } 372 | 373 | if(frameCounter <= 0) { 374 | if(gameOverY == 0) { 375 | currentPiece.erase(); 376 | currentPiece.moveDown(); 377 | currentPiece.draw(); 378 | } 379 | auto sb = board.score; 380 | bool donew = false; 381 | final switch (board.trySettle(currentPiece)) { 382 | case SettleStatus.none: 383 | break; 384 | case SettleStatus.settled: 385 | audio.beep(400); 386 | donew = true; 387 | break; 388 | case SettleStatus.cleared: 389 | audio.beep(1100); 390 | audio.beep(400); 391 | donew = true; 392 | break; 393 | case SettleStatus.tetris: 394 | audio.beep(1200); 395 | audio.beep(400); 396 | audio.beep(700); 397 | donew = true; 398 | break; 399 | case SettleStatus.gameOver: 400 | audio.boop(); 401 | 402 | auto painter = window.draw(); 403 | painter.outlineColor = Color.white; 404 | painter.fillColor = Color(127, 127, 127); 405 | painter.drawRectangle(Point(0, 0), Size(window.width, (gameOverY + 1) * PieceSize)); 406 | gameOverY++; 407 | break; 408 | } 409 | 410 | if(donew) { 411 | currentPiece.reset(Piece.randomType()); 412 | if(board.score != sb) 413 | board.redraw(window); 414 | } 415 | 416 | frameCounter = 2 * 5 * 3; 417 | } 418 | 419 | if(downPressed) 420 | frameCounter -= 12; 421 | frameCounter -= difficulty; 422 | }, (KeyEvent kev) { 423 | if(kev.key == Key.Down) 424 | downPressed = kev.pressed; 425 | if(!kev.pressed) return; 426 | switch(kev.key) { 427 | case Key.Up: 428 | case Key.Space: 429 | currentPiece.erase(); 430 | currentPiece.rotate(); 431 | currentPiece.draw(); 432 | audio.beep(1100); 433 | audio.beep(1000); 434 | break; 435 | case Key.Left: 436 | currentPiece.erase(); 437 | currentPiece.moveLeft(); 438 | currentPiece.draw(); 439 | break; 440 | case Key.Right: 441 | currentPiece.erase(); 442 | currentPiece.moveRight(); 443 | currentPiece.draw(); 444 | break; 445 | case Key.LeftBracket: 446 | if(difficulty) 447 | difficulty--; 448 | break; 449 | case Key.RightBracket: 450 | if(difficulty < 10) 451 | difficulty++; 452 | break; 453 | default: 454 | } 455 | }); 456 | 457 | import std.stdio; 458 | writeln(board.score); 459 | } 460 | --------------------------------------------------------------------------------