summaryrefslogtreecommitdiffhomepage
path: root/src/ext_depends
diff options
context:
space:
mode:
authorRalph Amissah <ralph.amissah@gmail.com>2025-08-28 11:05:29 -0400
committerRalph Amissah <ralph.amissah@gmail.com>2025-08-28 11:05:29 -0400
commit777102b2a42c20b5fc14fdf989d214c2376fe711 (patch)
treecb7aeed8f408a895d82ef5a4166e7d65b730fa38 /src/ext_depends
parentdub 1.40.0, llvm 20 (diff)
src/ext_deplends updated arsd:{core.d,cgi.d}HEADmain
Diffstat (limited to 'src/ext_depends')
-rw-r--r--src/ext_depends/arsd/cgi.d4
-rw-r--r--src/ext_depends/arsd/core.d755
2 files changed, 697 insertions, 62 deletions
diff --git a/src/ext_depends/arsd/cgi.d b/src/ext_depends/arsd/cgi.d
index 0af9f25..d1d2ad1 100644
--- a/src/ext_depends/arsd/cgi.d
+++ b/src/ext_depends/arsd/cgi.d
@@ -2735,7 +2735,7 @@ class Cgi {
}
// closing the last chunk...
- if(nph && rawDataOutput !is null && responseChunked)
+ if(requestMethod != RequestMethod.HEAD && nph && rawDataOutput !is null && responseChunked)
rawDataOutput(cast(const(ubyte)[]) "0\r\n\r\n");
if(flushDelegate)
@@ -3132,7 +3132,7 @@ struct Uri {
string scheme; /// e.g. "http" in "http://example.com/"
string userinfo; /// the username (and possibly a password) in the uri
- string host; /// the domain name
+ string host; /// the domain name. note it may be an ip address or have percent encoding too.
int port; /// port number, if given. Will be zero if a port was not explicitly given
string path; /// e.g. "/folder/file.html" in "http://example.com/folder/file.html"
string query; /// the stuff after the ? in a uri
diff --git a/src/ext_depends/arsd/core.d b/src/ext_depends/arsd/core.d
index 52f71ba..5fec19f 100644
--- a/src/ext_depends/arsd/core.d
+++ b/src/ext_depends/arsd/core.d
@@ -26,6 +26,20 @@
+/
module arsd.core;
+/+
+ Intended to be Supported OSes:
+ * Windows (at least Vista, MAYBE XP)
+ * Linux
+ * FreeBSD 14 (maybe 13 too)
+ * Mac OS
+
+ Eventually also:
+ * ios
+ * OpenBSD
+ * Android
+ * maybe apple watch os?
++/
+
static if(__traits(compiles, () { import core.interpolation; })) {
import core.interpolation;
@@ -42,6 +56,13 @@ static if(__traits(compiles, () { import core.interpolation; })) {
struct InterpolatedExpression(string code) {}
}
+// arsd core is now default but you can opt out for a lil while
+version(no_arsd_core) {
+
+} else {
+ version=use_arsd_core;
+}
+
version(use_arsd_core)
enum use_arsd_core = true;
else
@@ -90,6 +111,16 @@ else
version=OSXCocoa;
version(iOS)
version=OSXCocoa;
+ } else version(DigitalMars) {
+ version(OSX)
+ version=OSXCocoa;
+ version(iOS)
+ version=OSXCocoa;
+ } else version(LDC) {
+ version(OSX)
+ version=OSXCocoa;
+ version(iOS)
+ version=OSXCocoa;
}
version = HasFile;
@@ -127,9 +158,12 @@ import core.time;
version(OSXCocoa) {
version(ArsdNoCocoa)
enum bool UseCocoa = false;
- else
+ else {
+ version=UseCocoa;
enum bool UseCocoa = true;
-}
+ }
+} else
+ enum bool UseCocoa = false;
import core.attribute;
static if(!__traits(hasMember, core.attribute, "mustuse"))
@@ -189,28 +223,32 @@ version(Emscripten) {
// THIS FILE DOESN'T ACTUALLY EXIST, WE NEED TO MAKE IT
import core.sys.openbsd.sys.event;
} else version(OSX) {
- version=Arsd_core_kqueue;
+ version=Arsd_core_dispatch;
import core.sys.darwin.sys.event;
} else version(iOS) {
- version=Arsd_core_kqueue;
+ version=Arsd_core_dispatch;
import core.sys.darwin.sys.event;
}
// FIXME: pragma(linkerDirective, "-framework", "Cocoa") works in ldc
-version(OSXCocoa)
+static if(UseCocoa)
enum CocoaAvailable = true;
else
enum CocoaAvailable = false;
version(D_OpenD) {
- version(OSXCocoa)
+ static if(UseCocoa) {
pragma(linkerDirective, "-framework", "Cocoa");
+ pragma(linkerDirective, "-framework", "QuartzCore");
+ }
} else {
- version(OSXCocoa)
- version(LDC)
+ static if(UseCocoa)
+ version(LDC) {
pragma(linkerDirective, "-framework", "Cocoa");
+ pragma(linkerDirective, "-framework", "QuartzCore");
+ }
}
version(Posix) {
@@ -282,6 +320,55 @@ ref T reinterpretCast(T, V)(return ref V value) @system {
}
/++
+ Determines whether `needle` is a slice of `haystack`.
+
+ History:
+ Added on February 11, 2025.
+ +/
+bool isSliceOf(T1, T2)(scope const(T1)[] needle, scope const(T2)[] haystack) @trusted pure nothrow @nogc {
+ return (
+ needle.ptr >= haystack.ptr
+ && ((needle.ptr + needle.length) <= (haystack.ptr + haystack.length))
+ );
+}
+
+///
+@safe unittest {
+ string s0 = "01234";
+ const(char)[] s1 = s0[1 .. $];
+ const(void)[] s2 = s1.castTo!(const(void)[]);
+ string s3 = s1.idup;
+
+ assert( s0.isSliceOf(s0));
+ assert( s1.isSliceOf(s0));
+ assert( s2.isSliceOf(s0));
+ assert(!s3.isSliceOf(s0));
+
+ assert(!s0.isSliceOf(s1));
+ assert( s1.isSliceOf(s1));
+ assert( s2.isSliceOf(s1));
+ assert(!s3.isSliceOf(s1));
+
+ assert(!s0.isSliceOf(s2));
+ assert( s1.isSliceOf(s2));
+ assert( s2.isSliceOf(s2));
+ assert(!s3.isSliceOf(s2));
+
+ assert(!s0.isSliceOf(s3));
+ assert(!s1.isSliceOf(s3));
+ assert(!s2.isSliceOf(s3));
+ assert( s3.isSliceOf(s3));
+
+ assert(s1.length == 4);
+ assert(s1[0 .. 0].isSliceOf(s1));
+ assert(s1[0 .. 1].isSliceOf(s1));
+ assert(s1[1 .. 2].isSliceOf(s1));
+ assert(s1[1 .. 3].isSliceOf(s1));
+ assert(s1[1 .. $].isSliceOf(s1));
+ assert(s1[$ .. $].isSliceOf(s1));
+}
+
+/++
Does math as a 64 bit number, but saturates at int.min and int.max when converting back to a 32 bit int.
History:
@@ -374,6 +461,46 @@ struct stringz {
}
/+
+/++
+ A runtime tagged union, aka a sumtype.
+
+ History:
+ Added February 15, 2025
++/
+struct Union(T...) {
+ private uint contains_;
+ private union {
+ private T payload;
+ }
+
+ static foreach(index, type; T)
+ @implicit public this(type t) {
+ contains_ = index;
+ payload[index] = t;
+ }
+
+ bool contains(Part)() const {
+ static assert(indexFor!Part != -1);
+ return contains_ == indexFor!Part;
+ }
+
+ inout(Part) get(Part)() inout {
+ if(!contains!Part) {
+ throw new ArsdException!"Dynamic type mismatch"(indexFor!Part, contains_);
+ }
+ return payload[indexFor!Part];
+ }
+
+ private int indexFor(Part)() {
+ foreach(idx, thing; T)
+ static if(is(T == Part))
+ return idx;
+ return -1;
+ }
+}
++/
+
+/+
DateTime
year: 16 bits (-32k to +32k)
month: 4 bits
@@ -917,7 +1044,7 @@ struct LimitedVariant {
auto d = getDouble();
import core.stdc.stdio;
- char[128] buffer;
+ char[64] buffer;
auto count = snprintf(buffer.ptr, buffer.length, "%.17lf", d);
return buffer[0 .. count].idup;
case invalid:
@@ -1261,6 +1388,7 @@ struct CharzBuffer {
buffer[0 .. data.length] = data[];
buffer[data.length] = 0;
+ buffer = buffer[0 .. data.length + 1];
}
}
@@ -1441,19 +1569,31 @@ char[] intToString(long value, char[] buffer, IntToStringArgs args = IntToString
int start = pos;
int digitCount;
+ int groupCount;
do {
auto remainder = value % radix;
value = value / radix;
+ if(groupSize && groupCount == groupSize) {
+ buffer[pos++] = args.separator;
+ groupCount = 0;
+ }
+
buffer[pos++] = cast(char) (remainder < 10 ? (remainder + '0') : (remainder - 10 + args.ten));
+ groupCount++;
digitCount++;
} while(value);
if(digitsPad > 0) {
while(digitCount < digitsPad) {
+ if(groupSize && groupCount == groupSize) {
+ buffer[pos++] = args.separator;
+ groupCount = 0;
+ }
buffer[pos++] = args.padWith;
digitCount++;
+ groupCount++;
}
}
@@ -1504,6 +1644,105 @@ struct IntToStringArgs {
}
}
+struct FloatToStringArgs {
+ private {
+ // whole number component
+ ubyte padTo;
+ char padWith;
+ ubyte groupSize;
+ char separator;
+
+ // for the fractional component
+ ubyte minimumPrecision = 0; // will always show at least this many digits after the decimal (if it is 0 there may be no decimal)
+ ubyte maximumPrecision = 32; // will round to this many after the decimal
+
+ bool useScientificNotation; // if this is true, note the whole number component will always be exactly one digit, so the pad stuff applies to the exponent only and it assumes pad with zero's to two digits
+ }
+
+ FloatToStringArgs withPadding(int padTo, char padWith = '0') {
+ FloatToStringArgs args = this;
+ args.padTo = cast(ubyte) padTo;
+ args.padWith = padWith;
+ return args;
+ }
+
+ FloatToStringArgs withGroupSeparator(int groupSize, char separator = '_') {
+ FloatToStringArgs args = this;
+ args.groupSize = cast(ubyte) groupSize;
+ args.separator = separator;
+ return args;
+ }
+
+ FloatToStringArgs withPrecision(int minDigits, int maxDigits = 0) {
+ FloatToStringArgs args = this;
+ args.minimumPrecision = cast(ubyte) minDigits;
+ if(maxDigits < minDigits)
+ maxDigits = minDigits;
+ args.maximumPrecision = cast(ubyte) maxDigits;
+ return args;
+ }
+
+ FloatToStringArgs withScientificNotation(bool enabled) {
+ FloatToStringArgs args = this;
+ args.useScientificNotation = enabled;
+ return args;
+ }
+}
+
+char[] floatToString(double value, char[] buffer, FloatToStringArgs args = FloatToStringArgs.init) {
+ // actually doing this is pretty painful, so gonna pawn it off on the C lib
+ import core.stdc.stdio;
+ // FIXME: what if there's a locale in place that changes the decimal point?
+ auto ret = snprintf(buffer.ptr, buffer.length, args.useScientificNotation ? "%.*e" : "%.*f", args.maximumPrecision, value);
+ if(!args.useScientificNotation && (args.padTo || args.groupSize)) {
+ char[32] scratch = void;
+ auto idx = buffer[0 .. ret].indexOf(".");
+
+ int digitsOutput = 0;
+ int digitsGrouped = 0;
+ if(idx > 0) {
+ // there is a whole number component
+ int pos = cast(int) scratch.length;
+
+ auto splitPoint = idx;
+
+ while(idx) {
+ if(args.groupSize && digitsGrouped == args.groupSize) {
+ scratch[--pos] = args.separator;
+ digitsGrouped = 0;
+ }
+ scratch[--pos] = buffer[--idx];
+
+ digitsOutput++;
+ digitsGrouped++;
+ }
+
+ if(args.padTo)
+ while(digitsOutput < args.padTo) {
+ if(args.groupSize && digitsGrouped == args.groupSize) {
+ scratch[--pos] = args.separator;
+ digitsGrouped = 0;
+ }
+
+ scratch[--pos] = args.padWith;
+
+ digitsOutput++;
+ digitsGrouped++;
+ }
+
+ char[32] remainingBuffer;
+ remainingBuffer[0 .. ret - splitPoint]= buffer[splitPoint .. ret];
+
+ buffer[0 .. scratch.length - pos] = scratch[pos .. $];
+ buffer[scratch.length - pos .. scratch.length - pos + ret - splitPoint] = remainingBuffer[0 .. ret - splitPoint];
+
+ ret = cast(int) scratch.length - pos + ret - splitPoint;
+ }
+ }
+ // FIXME: if maximum precision....?
+ return buffer[0 .. ret];
+}
+
unittest {
char[32] buffer;
assert(intToString(0, buffer[]) == "0");
@@ -1521,6 +1760,24 @@ unittest {
assert(intToString(0xef1, buffer[], IntToStringArgs().withRadix(16).withPadding(8)) == "00000ef1");
assert(intToString(-0xef1, buffer[], IntToStringArgs().withRadix(16).withPadding(8)) == "-00000ef1");
assert(intToString(-0xef1, buffer[], IntToStringArgs().withRadix(16, 'A').withPadding(8, ' ')) == "- EF1");
+
+ assert(intToString(4000, buffer[], IntToStringArgs().withPadding(4).withGroupSeparator(3, ',')) == "4,000");
+ assert(intToString(400, buffer[], IntToStringArgs().withPadding(4).withGroupSeparator(3, ',')) == "0,400");
+
+ const pi = 3.14159256358979;
+ assert(floatToString(pi, buffer[], FloatToStringArgs().withPrecision(3)) == "3.142");
+ assert(floatToString(pi, buffer[], FloatToStringArgs().withPrecision(2)) == "3.14");
+ assert(floatToString(pi, buffer[], FloatToStringArgs().withPrecision(0)) == "3");
+
+ assert(floatToString(4.0, buffer[], FloatToStringArgs().withPrecision(0)) == "4");
+ assert(floatToString(4.0, buffer[], FloatToStringArgs().withPrecision(3)) == "4.000");
+
+ assert(floatToString(4.0, buffer[], FloatToStringArgs().withPadding(3).withPrecision(3)) == "004.000");
+ assert(floatToString(4.0, buffer[], FloatToStringArgs().withPadding(3).withGroupSeparator(3, ',').withPrecision(3)) == "004.000");
+ assert(floatToString(4.0, buffer[], FloatToStringArgs().withPadding(4).withGroupSeparator(3, ',').withPrecision(3)) == "0,004.000");
+ assert(floatToString(4000.0, buffer[], FloatToStringArgs().withPadding(4).withGroupSeparator(3, ',').withPrecision(3)) == "4,000.000");
+
+ assert(floatToString(pi*10, buffer[], FloatToStringArgs().withPrecision(2).withScientificNotation(true)) == "3.14e+01");
}
/++
@@ -1576,8 +1833,12 @@ inout(char)[] stripRightInternal(return inout(char)[] s) {
Moved from color.d to core.d in March 2023 (dub v11.0).
+/
string toStringInternal(T)(T t) {
- char[32] buffer;
- static if(is(T : string))
+ return writeGuts(null, null, null, false, &makeString, t);
+ /+
+ char[64] buffer;
+ static if(is(typeof(t.toString) : string))
+ return t.toString();
+ else static if(is(T : string))
return t;
else static if(is(T == enum)) {
switch(t) {
@@ -1599,10 +1860,15 @@ string toStringInternal(T)(T t) {
}
ret ~= "]";
return ret;
+ } else static if(is(T : double)) {
+ import core.stdc.stdio;
+ auto ret = snprintf(buffer.ptr, buffer.length, "%f", t);
+ return buffer[0 .. ret].idup;
} else {
static assert(0, T.stringof ~ " makes compile too slow");
// import std.conv; return to!string(t);
}
+ +/
}
/++
@@ -1808,7 +2074,7 @@ unittest {
assert(decodeUriComponent("+", true) == " ");
}
-private auto toDelegate(T)(T t) {
+public auto toDelegate(T)(T t) {
// static assert(is(T == function)); // lol idk how to do what i actually want here
static if(is(T Return == return))
@@ -1819,8 +2085,8 @@ private auto toDelegate(T)(T t) {
}
}
return &((cast(Wrapper*) t).call);
- } else static assert(0, "could not get params");
- else static assert(0, "could not get return value");
+ } else static assert(0, "could not get params; is it already a delegate you can pass directly?");
+ else static assert(0, "could not get return value, if it is a functor maybe try getting a delegate with `&yourobj.opCall` instead of toDelegate(yourobj)");
}
@system unittest {
@@ -2845,13 +3111,16 @@ interface ICoreEventLoop {
1: run before each loop OS wait call
2: run after each loop OS wait call
3: run both before and after each OS wait call
- 4: single shot?
- 8: no-coalesce? (if after was just run, it will skip the before loops unless this flag is set)
+ 4: single shot? NOT IMPLEMENTED
+ 8: no-coalesce? NOT IMPLEMENTED (if after was just run, it will skip the before loops unless this flag is set)
+ FIXME: it should return a handle you can use to unregister it
+/
void addDelegateOnLoopIteration(void delegate() dg, uint timingFlags);
final void addDelegateOnLoopIteration(void function() dg, uint timingFlags) {
+ if(timingFlags == 0)
+ assert(0, "would never run");
addDelegateOnLoopIteration(toDelegate(dg), timingFlags);
}
@@ -2875,6 +3144,8 @@ interface ICoreEventLoop {
version(Arsd_core_epoll) {
impl.unregisterFd(fd);
+ } else version(Arsd_core_dispatch) {
+ throw new NotYetImplementedException();
} else version(Arsd_core_kqueue) {
// intentionally blank - all registrations are one-shot there
// FIXME: actually it might not have gone off yet, in that case we do need to delete the filter
@@ -2904,6 +3175,8 @@ interface ICoreEventLoop {
version(Arsd_core_epoll) {
impl.unregisterFd(fd);
+ } else version(Arsd_core_dispatch) {
+ throw new NotYetImplementedException();
} else version(Arsd_core_kqueue) {
// intentionally blank - all registrations are one-shot there
// FIXME: actually it might not have gone off yet, in that case we do need to delete the filter
@@ -3885,20 +4158,51 @@ else class AsyncFile {
Tip: prefer the callback ones. If settings where async is possible, it will do async, and if not, it will sync.
- NOT IMPLEMENTED
+ NOT FULLY IMPLEMENTED
+/
void writeFile(string filename, const(void)[] contents) {
-
+ // FIXME: stop using the C lib and start error checking
+ import core.stdc.stdio;
+ CharzBuffer fn = filename;
+ auto file = fopen(fn.ptr, "wb");
+ if(file is null)
+ throw new ErrnoApiException("fopen", errno, [SavedArgument("filename", LimitedVariant(filename))]);
+ fwrite(contents.ptr, 1, contents.length, file);
+ fclose(file);
}
/// ditto
-string readTextFile(string filename, string fileEncoding = null) {
- return null;
+const(ubyte[]) readBinaryFile(string filename) {
+ // FIXME: stop using the C lib and check for more errors
+
+ import core.stdc.stdio;
+ CharzBuffer fn = filename;
+ auto file = fopen(fn.ptr, "rb");
+ if(file is null)
+ throw new ErrnoApiException("fopen", errno, [SavedArgument("filename", LimitedVariant(filename))]);
+ ubyte[] buffer = new ubyte[](64 * 1024);
+ ubyte[] contents;
+
+ while(true) {
+ auto ret = fread(buffer.ptr, 1, buffer.length, file);
+ if(ret < buffer.length) {
+ if(contents is null)
+ contents = buffer[0 .. ret];
+ else
+ contents ~= buffer[0 .. ret];
+ break;
+ } else {
+ contents ~= buffer[0 .. ret];
+ }
+ }
+ fclose(file);
+
+ return contents;
}
/// ditto
-const(ubyte[]) readBinaryFile(string filename) {
- return null;
+string readTextFile(string filename, string fileEncoding = null) {
+ return cast(string) readBinaryFile(filename);
}
/+
@@ -4430,7 +4734,8 @@ class Timer {
version(Windows) {} else
static void unregister(arsd.core.ICoreEventLoop.UnregisterToken urt) {
- urt.unregister();
+ if(urt.impl !is null)
+ urt.unregister();
}
@@ -4487,7 +4792,7 @@ class Timer {
CallbackHelper cbh;
} else version(linux) {
int fd = -1;
- } else version(OSXCocoa) {
+ } else static if(UseCocoa) {
} else static assert(0, "timer not supported");
}
@@ -6372,9 +6677,10 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
}
LoopIterationDelegate[] loopIterationDelegates;
- void runLoopIterationDelegates() {
+ void runLoopIterationDelegates(bool isAfter) {
foreach(lid; loopIterationDelegates)
- lid.dg();
+ if((!isAfter && (lid.flags & 1)) || (isAfter && (lid.flags & 2)))
+ lid.dg();
}
}
@@ -6382,12 +6688,98 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
loopIterationDelegates ~= LoopIterationDelegate(dg, timingFlags);
}
+ version(Arsd_core_dispatch) {
+
+ private NSRunLoop ttrl;
+
+ private this() {
+ ttrl = NSRunLoop.currentRunLoop;
+ }
+
+ // FIXME: this lies!! it runs until completion
+ RunOnceResult runOnce(Duration timeout = Duration.max) {
+ scope(exit) eventLoopRound++;
+
+ // FIXME: autorelease pool
+
+ if(false /*isWorker*/) {
+ runLoopIterationDelegates(false);
+
+ // FIXME: timeout is wrong
+ auto retValue = ttrl.runMode(NSDefaultRunLoopMode, beforeDate: NSDate.distantFuture);
+ if(retValue == false)
+ throw new Exception("could not start run loop");
+
+ runLoopIterationDelegates(true);
+
+ // NSApp.run();
+ // exitApplication();
+ //return RunOnceResult(RunOnceResult.Possibilities.GlobalExit);
+ return RunOnceResult(RunOnceResult.Possibilities.CarryOn);
+ } else {
+ // ui thread needs to pump nsapp events...
+ runLoopIterationDelegates(false);
+
+ auto timeoutNs = NSDate.distantFuture; // FIXME timeout here, future means no timeout
+
+ again:
+ NSEvent event = NSApp.nextEventMatchingMask(
+ NSEventMask.NSEventMaskAny,
+ timeoutNs,
+ NSDefaultRunLoopMode,
+ true
+ );
+ if(event !is null) {
+ NSApp.sendEvent(event);
+ timeoutNs = NSDate.distantPast; // only keep going if it won't block; we just want to clear the queue
+ goto again;
+ }
+
+ runLoopIterationDelegates(true);
+ return RunOnceResult(RunOnceResult.Possibilities.CarryOn);
+ }
+ }
+
+ UnregisterToken addCallbackOnFdReadable(int fd, CallbackHelper cb) {
+ auto input_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue());
+ // FIXME: can the GC reap this prematurely?
+ auto b = block(() {
+ cb.call();
+ });
+ // FIXME: should prolly free it eventually idk
+ import core.memory;
+ GC.addRoot(b);
+
+ dispatch_source_set_event_handler(input_src, b);
+ // dispatch_source_set_cancel_handler(input_src, ^{ close(my_file); });
+ dispatch_resume(input_src);
+
+ return UnregisterToken(this, fd, cb);
+
+ }
+ RearmToken addCallbackOnFdReadableOneShot(int fd, CallbackHelper cb) {
+ throw new NotYetImplementedException();
+ }
+ RearmToken addCallbackOnFdWritableOneShot(int fd, CallbackHelper cb) {
+ throw new NotYetImplementedException();
+ }
+ private void rearmFd(RearmToken token) {
+ if(token.readable)
+ cast(void) addCallbackOnFdReadableOneShot(token.fd, token.cb);
+ else
+ cast(void) addCallbackOnFdWritableOneShot(token.fd, token.cb);
+ }
+ }
+
version(Arsd_core_kqueue) {
// this thread apc dispatches go as a custom event to the queue
// the other queues go through one byte at a time pipes (barf). freebsd 13 and newest nbsd have eventfd too tho so maybe i can use them but the other kqueue systems don't.
RunOnceResult runOnce(Duration timeout = Duration.max) {
scope(exit) eventLoopRound++;
+
+ runLoopIterationDelegates(false);
+
kevent_t[16] ev;
//timespec tout = timespec(1, 0);
auto nev = kevent(kqueuefd, null, 0, ev.ptr, ev.length, null/*&tout*/);
@@ -6410,7 +6802,7 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
}
}
- runLoopIterationDelegates();
+ runLoopIterationDelegates(true);
return RunOnceResult(RunOnceResult.Possibilities.CarryOn);
}
@@ -6557,6 +6949,9 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
RunOnceResult runOnce(Duration timeout = Duration.max) {
scope(exit) eventLoopRound++;
+
+ runLoopIterationDelegates(false);
+
if(isWorker) {
// this function is only supported on Windows Vista and up, so using this
// means dropping support for XP.
@@ -6607,7 +7002,7 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
}
}
- runLoopIterationDelegates();
+ runLoopIterationDelegates(true);
return RunOnceResult(RunOnceResult.Possibilities.CarryOn);
}
@@ -6893,6 +7288,9 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
RunOnceResult runOnce(Duration timeout = Duration.max) {
scope(exit) eventLoopRound++;
+
+ runLoopIterationDelegates(false);
+
epoll_event[16] events;
auto ret = epoll_wait(epollfd, events.ptr, cast(int) events.length, -1); // FIXME: timeout
if(ret == -1) {
@@ -6916,7 +7314,7 @@ private class CoreEventLoopImplementation : ICoreEventLoop {
}
}
- runLoopIterationDelegates();
+ runLoopIterationDelegates(true);
return RunOnceResult(RunOnceResult.Possibilities.CarryOn);
}
@@ -8196,7 +8594,7 @@ class LoggerOf(T, size_t bufferSize = 16) {
int missedMessages = 0;
long n;
synchronized(logger) {
- while(logger.active && connected && logger.writeBufferPosition < readBufferPosition) {
+ while(logger.active && connected && logger.writeBufferPosition <= readBufferPosition) {
logger.condition.wait();
}
@@ -8210,15 +8608,15 @@ class LoggerOf(T, size_t bufferSize = 16) {
auto startPos = readBufferPosition % bufferSize;
auto endPos = us.writeBufferPosition % bufferSize;
if(endPos > startPos) {
- buffer[0 .. n] = us.ring[startPos .. endPos];
+ buffer[0 .. cast(size_t) n] = us.ring[cast(size_t) startPos .. cast(size_t) endPos];
} else {
auto ourSplit = us.ring.length - startPos;
- buffer[0 .. ourSplit] = us.ring[startPos .. $];
- buffer[ourSplit .. ourSplit + endPos] = us.ring[0 .. endPos];
+ buffer[0 .. cast(size_t) ourSplit] = us.ring[cast(size_t) startPos .. $];
+ buffer[cast(size_t) ourSplit .. cast(size_t) (ourSplit + endPos)] = us.ring[0 .. cast(size_t) endPos];
}
readBufferPosition = us.writeBufferPosition;
}
- foreach(item; buffer[0 .. n]) {
+ foreach(item; buffer[0 .. cast(size_t) n]) {
if(!connected)
break;
dg(item, missedMessages);
@@ -8382,6 +8780,16 @@ shared(LoggerOf!GenericEmbeddableInterpolatedSequence) logger() {
return _commonLogger;
}
+/++
+ Makes note of an exception you catch and otherwise ignore.
+
+ History:
+ Added April 17, 2025
++/
+void logSwallowedException(Exception e) {
+ logger.error(InterpolationHeader(), e.toString(), InterpolationFooter());
+}
+
/+
// using this requires a newish compiler so we just uncomment when necessary
unittest {
@@ -8649,12 +9057,35 @@ private void appendToBuffer(ref char[] buffer, ref int pos, scope const(char)[]
}
private void appendToBuffer(ref char[] buffer, ref int pos, long what) {
- if(buffer.length < pos + 16)
- buffer.length = pos + 16;
+ if(buffer.length < pos + 32)
+ buffer.length = pos + 32;
auto sliced = intToString(what, buffer[pos .. $]);
pos += sliced.length;
}
+private void appendToBuffer(ref char[] buffer, ref int pos, double what) {
+ if(buffer.length < pos + 32)
+ buffer.length = pos + 32;
+ auto sliced = floatToString(what, buffer[pos .. $]);
+ pos += sliced.length;
+}
+
+
+/++
+ You can use `mixin(dumpParams);` to put out a debug print of your current function call w/ params.
++/
+enum string dumpParams = q{
+ {
+ import arsd.core;
+ arsd.core.dumpParamsImpl(__FUNCTION__, __traits(parameters));
+ }
+};
+
+/// Don't call this directly, use `mixin(dumpParams);` instead
+public void dumpParamsImpl(T...)(string func, T args) {
+ writeGuts(func ~ "(", ")\n", ", ", false, &actuallyWriteToStdout, args);
+}
+
/++
A `writeln` that actually works, at least for some basic types.
@@ -8662,16 +9093,52 @@ private void appendToBuffer(ref char[] buffer, ref int pos, long what) {
This always does text. See also WritableStream and WritableTextStream when they are implemented.
+/
-void writeln(bool printInterpolatedCode = false, T...)(T t) {
+void writeln(T...)(T t) {
+ writeGuts(null, "\n", null, false, &actuallyWriteToStdout, t);
+}
+
+///
+void writelnStderr(T...)(T t) {
+ writeGuts(null, "\n", null, false, &actuallyWriteToStderr, t);
+}
+
+/++
+
++/
+package(arsd) string enumNameForValue(T)(T t) {
+ switch(t) {
+ foreach(memberName; __traits(allMembers, T)) {
+ case __traits(getMember, T, memberName):
+ return memberName;
+ }
+ default:
+ return "<unknown>";
+ }
+}
+
+/+
+ Purposes:
+ * debugging
+ * writing
+ * converting single value to string?
++/
+private string writeGuts(T...)(string prefix, string suffix, string argSeparator, bool printInterpolatedCode, string function(scope char[] result) writer, T t) {
char[256] bufferBacking;
char[] buffer = bufferBacking[];
int pos;
- foreach(arg; t) {
+ if(prefix.length)
+ appendToBuffer(buffer, pos, prefix);
+
+ foreach(i, arg; t) {
+ static if(i)
+ if(argSeparator.length)
+ appendToBuffer(buffer, pos, argSeparator);
+
static if(is(typeof(arg) Base == enum)) {
appendToBuffer(buffer, pos, typeof(arg).stringof);
appendToBuffer(buffer, pos, ".");
- appendToBuffer(buffer, pos, toStringInternal(arg));
+ appendToBuffer(buffer, pos, enumNameForValue(arg));
appendToBuffer(buffer, pos, "(");
appendToBuffer(buffer, pos, cast(Base) arg);
appendToBuffer(buffer, pos, ")");
@@ -8682,13 +9149,9 @@ void writeln(bool printInterpolatedCode = false, T...)(T t) {
} else static if(is(typeof(arg) : long)) {
appendToBuffer(buffer, pos, arg);
} else static if(is(typeof(arg) : double)) {
- import core.stdc.stdio;
- char[128] fb;
- auto count = snprintf(fb.ptr, fb.length, "%.4lf", arg);
-
- appendToBuffer(buffer, pos, fb[0 .. count]);
+ appendToBuffer(buffer, pos, arg);
} else static if(is(typeof(arg) == InterpolatedExpression!code, string code)) {
- static if(printInterpolatedCode) {
+ if(printInterpolatedCode) {
appendToBuffer(buffer, pos, code);
appendToBuffer(buffer, pos, " = ");
}
@@ -8718,34 +9181,50 @@ void writeln(bool printInterpolatedCode = false, T...)(T t) {
}
}
- appendToBuffer(buffer, pos, "\n");
+ if(suffix.length)
+ appendToBuffer(buffer, pos, suffix);
- actuallyWriteToStdout(buffer[0 .. pos]);
+ return writer(buffer[0 .. pos]);
}
debug void dump(T...)(T t, string file = __FILE__, size_t line = __LINE__) {
- writeln!true(file, ":", line, ": ", t);
-}
+ string separator;
+ static if(T.length && is(T[0] == InterpolationHeader))
+ separator = null;
+ else
+ separator = "; ";
-private void actuallyWriteToStdout(scope char[] buffer) @trusted {
+ writeGuts(file ~ ":" ~ toStringInternal(line) ~ ": ", "\n", separator, true, &actuallyWriteToStdout, t);
+}
+private string makeString(scope char[] buffer) @safe {
+ return buffer.idup;
+}
+private string actuallyWriteToStdout(scope char[] buffer) @safe {
+ return actuallyWriteToStdHandle(1, buffer);
+}
+private string actuallyWriteToStderr(scope char[] buffer) @safe {
+ return actuallyWriteToStdHandle(2, buffer);
+}
+private string actuallyWriteToStdHandle(int whichOne, scope char[] buffer) @trusted {
version(UseStdioWriteln)
{
import std.stdio;
- writeln(buffer);
+ (whichOne == 1 ? stdout : stderr).writeln(buffer);
}
else version(Windows) {
import core.sys.windows.wincon;
- auto hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ auto h = whichOne == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE;
+
+ auto hStdOut = GetStdHandle(h);
if(hStdOut == null || hStdOut == INVALID_HANDLE_VALUE) {
AllocConsole();
- hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ hStdOut = GetStdHandle(h);
}
if(GetFileType(hStdOut) == FILE_TYPE_CHAR) {
- wchar[256] wbuffer;
- auto toWrite = makeWindowsString(buffer, wbuffer, WindowsStringConversionFlags.convertNewLines);
+ WCharzBuffer toWrite = WCharzBuffer(buffer, WindowsStringConversionFlags.convertNewLines);
DWORD written;
WriteConsoleW(hStdOut, toWrite.ptr, cast(DWORD) toWrite.length, &written, null);
@@ -8755,8 +9234,10 @@ private void actuallyWriteToStdout(scope char[] buffer) @trusted {
}
} else {
import unix = core.sys.posix.unistd;
- unix.write(1, buffer.ptr, buffer.length);
+ unix.write(whichOne, buffer.ptr, buffer.length);
}
+
+ return null;
}
/+
@@ -9153,12 +9634,14 @@ package(arsd) version(Windows) extern(Windows) {
int WSARecvFrom(SOCKET, LPWSABUF, DWORD, LPDWORD, LPDWORD, sockaddr*, LPINT, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE);
}
-package(arsd) version(OSXCocoa) {
+package(arsd) version(UseCocoa) {
/* Copy/paste chunk from Jacob Carlborg { */
// from https://raw.githubusercontent.com/jacob-carlborg/druntime/550edd0a64f0eb2c4f35d3ec3d88e26b40ac779e/src/core/stdc/clang_block.d
// with comments stripped (see docs in the original link), code reformatted, and some names changed to avoid potential conflicts
+// note these should always be passed by pointer!
+
import core.stdc.config;
struct ObjCBlock(R = void, Params...) {
private:
@@ -9177,18 +9660,30 @@ private:
this.isa = isa;
this.flags = flags;
this.invoke = invoke;
- this.dg = dg;
this.descriptor = &.objcblock_descriptor;
+
+ // FIXME: is this needed or not? it could be held by the OS and not be visible to GC i think
+ // import core.memory; GC.addRoot(dg.ptr);
+
+ this.dg = dg;
}
}
-ObjCBlock!(R, Params) block(R, Params...)(R delegate(Params) dg) {
+ObjCBlock!(R, Params) blockOnStack(R, Params...)(R delegate(Params) dg) {
static if (Params.length == 0)
- enum flags = 0x50000000;
+ enum flags = 0x50000000;
else
enum flags = 0x40000000;
return ObjCBlock!(R, Params)(&_NSConcreteStackBlock, flags, &objcblock_invoke!(R, Params), dg);
}
+ObjCBlock!(R, Params)* block(R, Params...)(R delegate(Params) dg) {
+ static if (Params.length == 0)
+ enum flags = 0x50000000;
+ else
+ enum flags = 0x40000000;
+
+ return new ObjCBlock!(R, Params)(&_NSConcreteStackBlock, flags, &objcblock_invoke!(R, Params), dg);
+}
private struct Descriptor {
c_ulong reserved;
@@ -9279,8 +9774,19 @@ If you are not sure if Cocoa thinks your application is multithreaded or not, yo
void getCharacters(wchar* buffer, NSRange range) @selector("getCharacters:range:");
bool getBytes(void* buffer, NSUInteger maxBufferCount, NSUInteger* usedBufferCount, NSStringEncoding encoding, NSStringEncodingConversionOptions options, NSRange range, NSRange* leftover) @selector("getBytes:maxLength:usedLength:encoding:options:range:remainingRange:");
+
+ CGSize sizeWithAttributes(NSDictionary attrs) @selector("sizeWithAttributes:");
+ }
+
+ // FIXME: it is a generic in objc with <KeyType, ObjectType>
+ extern class NSDictionary : NSObject {
+ static NSDictionary dictionaryWithObject(NSObject object, NSid key) @selector("dictionaryWithObject:forKey:");
+ // static NSDictionary initWithObjects(NSArray objects, NSArray forKeys) @selector("initWithObjects:forKeys:");
}
+ alias NSAttributedStringKey = NSString;
+ /* const */extern __gshared NSAttributedStringKey NSFontAttributeName;
+
struct NSRange {
NSUInteger loc;
NSUInteger len;
@@ -9406,7 +9912,42 @@ If you are not sure if Cocoa thinks your application is multithreaded or not, yo
void run() @selector("run");
+ void stop(NSid sender) @selector("stop:");
+
+ void finishLaunching() @selector("finishLaunching");
+
void terminate(void*) @selector("terminate:");
+
+ void sendEvent(NSEvent event) @selector("sendEvent:");
+ NSEvent nextEventMatchingMask(
+ NSEventMask mask,
+ NSDate untilDate,
+ NSRunLoopMode inMode,
+ bool dequeue
+ ) @selector("nextEventMatchingMask:untilDate:inMode:dequeue:");
+ }
+
+ enum NSEventMask : ulong {
+ NSEventMaskAny = ulong.max
+ }
+
+ version(OSX)
+ extern class NSRunLoop : NSObject {
+ static @property NSRunLoop currentRunLoop() @selector("currentRunLoop");
+ static @property NSRunLoop mainRunLoop() @selector("mainRunLoop");
+ bool runMode(NSRunLoopMode mode, NSDate beforeDate) @selector("runMode:beforeDate:");
+ }
+
+ alias NSRunLoopMode = NSString;
+
+ extern __gshared NSRunLoopMode NSDefaultRunLoopMode;
+
+ version(OSX)
+ extern class NSDate : NSObject {
+ static @property NSDate distantFuture() @selector("distantFuture");
+ static @property NSDate distantPast() @selector("distantPast");
+ static @property NSDate now() @selector("now");
+
}
version(OSX)
@@ -9523,6 +10064,7 @@ If you are not sure if Cocoa thinks your application is multithreaded or not, yo
NSRect frame() @selector("frame");
NSRect contentRectForFrameRect(NSRect frameRect) @selector("contentRectForFrameRect:");
+ NSRect frameRectForContentRect(NSRect contentRect) @selector("frameRectForContentRect:");
NSString title() @selector("title");
void title(NSString value) @selector("setTitle:");
@@ -9533,6 +10075,8 @@ If you are not sure if Cocoa thinks your application is multithreaded or not, yo
void delegate_(NSWindowDelegate) @selector("setDelegate:");
void setBackgroundColor(NSColor color) @selector("setBackgroundColor:");
+
+ void setIsVisible(bool b) @selector("setIsVisible:");
}
version(OSX)
@@ -9749,4 +10293,95 @@ If you are not sure if Cocoa thinks your application is multithreaded or not, yo
extern(C) __gshared void* _D4arsd4core6NSView7__ClassZ = null;
extern(C) __gshared void* _D4arsd4core8NSWindow7__ClassZ = null;
}
+
+
+
+ extern(C) { // grand central dispatch bindings
+
+ // /Library/Developer/CommandLineTools/SDKs/MacOSX13.1.sdk/usr/include/dispatch
+ // https://swiftlang.github.io/swift-corelibs-libdispatch/tutorial/
+ // https://man.freebsd.org/cgi/man.cgi?query=dispatch_main&sektion=3&apropos=0&manpath=macOS+14.3.1
+
+ struct dispatch_source_type_s {}
+ private __gshared immutable extern {
+ dispatch_source_type_s _dispatch_source_type_timer;
+ dispatch_source_type_s _dispatch_source_type_proc;
+ dispatch_source_type_s _dispatch_source_type_signal;
+ dispatch_source_type_s _dispatch_source_type_read;
+ dispatch_source_type_s _dispatch_source_type_write;
+ dispatch_source_type_s _dispatch_source_type_vnode;
+ // also memory pressure and some others
+ }
+
+ immutable DISPATCH_SOURCE_TYPE_TIMER = &_dispatch_source_type_timer;
+ immutable DISPATCH_SOURCE_TYPE_PROC = &_dispatch_source_type_proc;
+ immutable DISPATCH_SOURCE_TYPE_SIGNAL = &_dispatch_source_type_signal;
+ immutable DISPATCH_SOURCE_TYPE_READ = &_dispatch_source_type_read;
+ immutable DISPATCH_SOURCE_TYPE_WRITE = &_dispatch_source_type_write;
+ immutable DISPATCH_SOURCE_TYPE_VNODE = &_dispatch_source_type_vnode;
+ // also are some for internal data change things and a couple others
+
+ enum DISPATCH_PROC_EXIT = 0x80000000; // process exited
+ enum DISPATCH_PROC_FORK = 0x40000000; // it forked
+ enum DISPATCH_PROC_EXEC = 0x20000000; // it execed
+ enum DISPATCH_PROC_SIGNAL = 0x08000000; // it received a signal
+
+ enum DISPATCH_VNODE_DELETE = 0x1;
+ enum DISPATCH_VNODE_WRITE = 0x2;
+ enum DISPATCH_VNODE_EXTEND = 0x4;
+ enum DISPATCH_VNODE_ATTRIB = 0x8;
+ enum DISPATCH_VNODE_LINK = 0x10;
+ enum DISPATCH_VNODE_RENAME = 0x20;
+ enum DISPATCH_VNODE_REVOKE = 0x40;
+ enum DISPATCH_VNODE_FUNLOCK = 0x100;
+
+ private struct dispatch_source_s;
+ private struct dispatch_queue_s {}
+
+ alias dispatch_source_type_t = const(dispatch_source_type_s)*;
+
+ alias dispatch_source_t = dispatch_source_s*; // NSObject<OS_dispatch_source>
+ alias dispatch_queue_t = dispatch_queue_s*; // NSObject<OS_dispatch_queue>
+ alias dispatch_object_t = void*; // actually a "transparent union" of the dispatch_source_t, dispatch_queue_t, and others
+ alias dispatch_block_t = ObjCBlock!(void)*;
+ static if(void*.sizeof == 8)
+ alias uintptr_t = ulong;
+ else
+ alias uintptr_t = uint;
+
+ dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, c_ulong mask, dispatch_queue_t queue);
+ void dispatch_source_set_event_handler(dispatch_source_t source, dispatch_block_t handler);
+ void dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t handler);
+ void dispatch_source_cancel(dispatch_source_t source);
+
+ // DISPATCH_DECL_SUBCLASS(dispatch_queue_main, dispatch_queue_serial);
+ // dispatch_queue_t dispatch_get_main_queue();
+
+ extern __gshared dispatch_queue_s _dispatch_main_q;
+
+ extern(D) dispatch_queue_t dispatch_get_main_queue() {
+ return &_dispatch_main_q;
+ }
+
+ // FIXME: what is dispatch_time_t ???
+ // dispatch_time
+ // dispatch_walltime
+
+ // void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, ulong interval, ulong leeway);
+
+ void dispatch_retain(dispatch_object_t object);
+ void dispatch_release(dispatch_object_t object);
+
+ void dispatch_resume(dispatch_object_t object);
+ void dispatch_pause(dispatch_object_t object);
+
+ void* dispatch_get_context(dispatch_object_t object);
+ void dispatch_set_context(dispatch_object_t object, void* context);
+
+ // sends a function to the given queue
+ void dispatch_sync(dispatch_queue_t queue, scope dispatch_block_t block);
+ void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
+
+ } // grand central dispatch bindings
+
}