├── .gitignore ├── Interfaces.md ├── Print.md ├── README.md ├── druntime └── object.d ├── examples ├── fileexample.d ├── helloworld.d └── messagebox.d ├── src └── mar │ ├── aliasseq.d │ ├── array.d │ ├── arraybuilder.d │ ├── ascii.d │ ├── bitarray.d │ ├── c.d │ ├── cannotfail.d │ ├── cmdopt.d │ ├── conv.d │ ├── ctypes.d │ ├── disk │ └── mbr.d │ ├── endian.d │ ├── enforce.d │ ├── env.d │ ├── expect.d │ ├── file │ ├── package.d │ └── perm.d │ ├── filesys.d │ ├── findprog.d │ ├── flag.d │ ├── from.d │ ├── input.d │ ├── intfromchars.d │ ├── json.d │ ├── linux │ ├── capability.d │ ├── cthunk │ │ ├── .gitignore │ │ ├── gen.sh │ │ ├── gencthunk.c │ │ └── package.d │ ├── file │ │ ├── package.d │ │ └── perm.d │ ├── filesys.d │ ├── ioctl.d │ ├── mem.d │ ├── mmap.d │ ├── process.d │ ├── signals.d │ ├── stdio.d │ ├── syscall.d │ ├── ttyioctl.d │ └── vt.d │ ├── math.d │ ├── mem │ ├── mmap.d │ ├── package.d │ ├── sbrk.d │ └── windowsheap.d │ ├── mmap.d │ ├── octal.d │ ├── passfail.d │ ├── path.d │ ├── print │ ├── package.d │ ├── printers.d │ └── sprint.d │ ├── process.d │ ├── qual.d │ ├── ryu.d │ ├── sentinel.d │ ├── serialize.d │ ├── start.d │ ├── stdio.d │ ├── string.d │ ├── sync.d │ ├── thread.d │ ├── time.d │ ├── traits.d │ ├── typecons.d │ ├── windows │ ├── coreaudio.d │ ├── file.d │ ├── filesys.d │ ├── kernel32 │ │ ├── link.d │ │ ├── nolink.d │ │ └── package.d │ ├── mmap.d │ ├── mscoree │ │ ├── link.d │ │ ├── nolink.d │ │ └── package.d │ ├── ole32 │ │ ├── link.d │ │ ├── nolink.d │ │ └── package.d │ ├── package.d │ ├── stdio.d │ ├── user32 │ │ ├── link.d │ │ ├── nolink.d │ │ └── package.d │ └── winmm │ │ ├── link.d │ │ ├── mmreg.d │ │ ├── mmsystem.d │ │ ├── nolink.d │ │ └── package.d │ └── wrap.d └── test └── go.d /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | /test/out 4 | -------------------------------------------------------------------------------- /Interfaces.md: -------------------------------------------------------------------------------- 1 | ```D 2 | 3 | 4 | // The FileD type 5 | // 6 | struct FileD 7 | { 8 | WriteResult tryWrite(void* ptr, size_t length); 9 | WriteResult tryWrite(void[] array); 10 | } 11 | 12 | auto result = filed.tryWrite(data); 13 | 14 | /* 15 | result.passed, returns true if all the data was successfully written 16 | result.failed, returns true if all data was not written 17 | result.onFailWritten, ONLY CALL IF failed IS TRUE 18 | returns the amount of data that was written 19 | result.errorCode, returns the error code, only valid is failed is true 20 | */ 21 | 22 | 23 | 24 | ``` -------------------------------------------------------------------------------- /Print.md: -------------------------------------------------------------------------------- 1 | # Print API 2 | 3 | Mar has its own API to support data conversion to text. 4 | 5 | ### Printer 6 | 7 | A _Printer_ is an object that accepts text and/or text operations to print. It must have the following methods: 8 | 9 | ```D 10 | alias PutResult = ; 11 | static PutResult success(); 12 | PutResult flush(); 13 | PutResult put(const(char)[] str); 14 | PutResult putc(const char c); 15 | 16 | auto getTempBuffer(size_t size)(); 17 | auto tryGetTempBufferImpl(size_t size); 18 | void commitBuffer(DefaultPrinterBuffer buf); 19 | ``` 20 | 21 | ### Print Method 22 | 23 | To allow an object to be converted to text, it must implement the `print` function, i.e. 24 | 25 | ```D 26 | struct Point 27 | { 28 | int x; 29 | int y; 30 | auto print(P)(P printer) const 31 | { 32 | return printArgs(printer, x, ',', y); 33 | } 34 | } 35 | ``` 36 | 37 | ### sprint functions 38 | 39 | ```D 40 | // print args into dest buffer 41 | char[] sprint(T...)(char[] dest, T args); 42 | // use sprintJustReturnSize to just return the size printed in dest 43 | 44 | // get print size by printing to a "no-op" printer and tracking the number of characters printed 45 | size_t getPrintSize(T...)(T args); 46 | 47 | // print and allocate a string for args 48 | char[] sprintMallocNoSentinel(T...)(T args); 49 | 50 | // print and allocate a string for args that is null-terminated 51 | SentinelArray!char sprintMallocSentinel(T...)(T args); 52 | ``` 53 | 54 | ### Multiple print methods 55 | 56 | By default the `print` method will be used on an object, but you can support multiple formats, i.e. 57 | 58 | ```D 59 | static struct Point 60 | { 61 | int x; 62 | int y; 63 | auto print(P)(P printer) const 64 | { 65 | return printArgs(printer, x, ',', y); 66 | } 67 | auto formatHex() const 68 | { 69 | static struct Print 70 | { 71 | const(Point)* p; 72 | auto print(P)(P printer) const 73 | { 74 | return printArgs(printer, "0x", 75 | mar.print.formatHex(p.x), ",0x", .mar.print.formatHex(p.y)); 76 | } 77 | } 78 | return Print(&this); 79 | } 80 | } 81 | 82 | auto p = Point(10, 18); 83 | stdout.writeln(p); // prints "10,18" 84 | stdout.writeln(p.formatHex); // prints "0xa,0x12" 85 | ``` 86 | 87 | ### Template Bloat 88 | 89 | If you have a large project and are concerned with the template bloat from having alot of `print` method/`printer` combinations you can alleviate this by reducing the number of printers. You can reduce the number of printers by wrapping multiple kinds of printers with a common type. In fact, you could wrap all printers with a common interface which would cause only 1 `print` method to be instantiated per object. This API allows applications to have full control over the balance between "code specialization" and "code bloat". 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mar 2 | 3 | An alternative D standard library. 4 | 5 | # Goals 6 | 7 | * Work without the C standard library and without druntime 8 | 9 | > Note: it should also work with the C standard library via a "version" switch 10 | 11 | Note that this also implies it works without the GC. 12 | 13 | * Work with "Better C" 14 | * Expose both "platform-specific" and "platform-agnostic" apis. 15 | * Levarage D's type systems to create APIs with strong safety guarantees. 16 | 17 | An example of this is the `SentinelPtr` template that results in a pointer to an array that guarantees the array ends with a "sentinel value". The classic example of this is a "c string" which ends with a null character. By using `SentinelPtr` in an API, it documents this requirement and enforces it at compile time. This also facilitates better interoperation with C as it allows D to work with C strings making it unnecessary to marshal strings to temporary buffers every time they are passed to a C function. 18 | 19 | # Build 20 | 21 | Currently there's no build. If you want to use it, you can add the following arguments to your compiler command: 22 | ``` 23 | -I=/src -i=mar 24 | ``` 25 | > `-I= `-i=mar` compile all imported modules from the mar package 27 | 28 | This will cause the compiler to compile any modules from mar alongside your own application. 29 | 30 | # Test 31 | 32 | ``` 33 | cd test 34 | ./go.d 35 | ``` 36 | 37 | # Versions 38 | 39 | #### `-version=NoStdc` 40 | 41 | Do not use the standard C library 42 | 43 | #### `-version=NoExit` 44 | 45 | Do not allow calls to `exit`. Reduces the number of ways the program can quit. Useful if program needs to clean up before exiting. 46 | -------------------------------------------------------------------------------- /examples/fileexample.d: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rund 2 | //!importPath ../src 3 | //!importPath ../druntime 4 | //!version NoStdc 5 | //!noConfigFile 6 | //!betterC 7 | //!debug 8 | //!debugSymbols 9 | import mar.sentinel; 10 | import mar.c; 11 | import mar.file; 12 | import mar.stdio; 13 | 14 | import mar.start; 15 | mixin(startMixin); 16 | 17 | extern (C) int main(uint argc, SentinelPtr!cstring argv, SentinelPtr!cstring envp) 18 | { 19 | auto filename = lit!"generatedfile.txt"; 20 | stdout.writeln("Generating '", filename, "'..."); 21 | auto file = tryOpenFile(filename.ptr, OpenFileOpt(OpenAccess.writeOnly) 22 | .createOrTruncate 23 | .mode(ModeSet.rwUser | ModeSet.rwGroup | ModeFlags.readOther)); 24 | file.writeln("This file was generated using the mar library"); 25 | file.close(); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /examples/helloworld.d: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rund 2 | //!importPath ../src 3 | //!importPath ../druntime 4 | //!version NoStdc 5 | //!noConfigFile 6 | //!betterC 7 | //!debug 8 | //!debugSymbols 9 | 10 | /** 11 | This example hello world program will compile without any libraries, no C 12 | standard library, no D runtime, not even the library that contains the `_start` 13 | symbol. 14 | 15 | NOTE: only x86_64 is currently supported 16 | */ 17 | import mar.sentinel; 18 | import mar.c; 19 | import mar.stdio; 20 | 21 | import mar.start; 22 | mixin(startMixin); 23 | 24 | extern (C) int main(uint argc, SentinelPtr!cstring argv, SentinelPtr!cstring envp) 25 | { 26 | stdout.write("Hello World!\n"); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /examples/messagebox.d: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rund 2 | //!importPath ../src 3 | //!importPath ../druntime 4 | //!version NoStdc 5 | //!noConfigFile 6 | //!betterC 7 | //!debug 8 | //!debugSymbols 9 | 10 | import mar.sentinel; 11 | import mar.c; 12 | import mar.windows.types; 13 | import mar.windows.user32; 14 | 15 | //import mar.start; 16 | //mixin(startMixin); 17 | 18 | extern (C) int main(uint argc, SentinelPtr!cstring argv, SentinelPtr!cstring envp) 19 | { 20 | MessageBoxA(GetActiveWindow(), "the text", "the caption", MessageBoxType.ok); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /src/mar/aliasseq.d: -------------------------------------------------------------------------------- 1 | module mar.aliasseq; 2 | 3 | alias AliasSeq(T...) = T; 4 | -------------------------------------------------------------------------------- /src/mar/arraybuilder.d: -------------------------------------------------------------------------------- 1 | module mar.arraybuilder; 2 | 3 | import mar.expect : MemoryResult; 4 | 5 | struct ArrayBuilder(T, Policy = MallocArrayBuilderPolicy!32) 6 | { 7 | private T[] buffer; 8 | private size_t _length; 9 | 10 | auto ref opIndex(size_t index) inout { return buffer[index]; } 11 | T[] data() const { pragma(inline, true); return (cast(T[])buffer)[0 .. _length]; } 12 | size_t length() const { return _length; } 13 | 14 | void shrinkTo(size_t newLength) 15 | in { assert(newLength <= _length); } do 16 | { 17 | this._length = newLength; 18 | } 19 | 20 | MemoryResult tryPut(T item) 21 | { 22 | if (_length == buffer.length) 23 | { 24 | auto result = Policy.increaseBuffer!T(buffer, _length + 1, _length); 25 | if (result.length < _length + 1) 26 | return MemoryResult.outOfMemory; 27 | this.buffer = result; 28 | } 29 | buffer[_length++] = item; 30 | return MemoryResult.success; 31 | } 32 | MemoryResult tryPutRange(U)(U[] items) 33 | { 34 | import mar.array : acopy; 35 | 36 | auto lengthNeeded = _length + items.length; 37 | if (lengthNeeded > buffer.length) 38 | { 39 | auto result = Policy.increaseBuffer!T(buffer, lengthNeeded, _length); 40 | if (result.length < lengthNeeded) 41 | return MemoryResult.outOfMemory; 42 | this.buffer = result; 43 | } 44 | acopy(buffer.ptr + _length, items); 45 | _length += items.length; 46 | return MemoryResult.success; 47 | } 48 | void removeAt(size_t index) 49 | { 50 | for (size_t i = index; i + 1 < _length; i++) 51 | { 52 | buffer[i] = buffer[i+1]; 53 | } 54 | _length--; 55 | } 56 | 57 | auto pop() 58 | { 59 | auto result = buffer[_length-1]; 60 | _length--; 61 | return result; 62 | } 63 | 64 | void free() 65 | { 66 | Policy.free(buffer); 67 | buffer = null; 68 | _length = 0; 69 | } 70 | } 71 | 72 | struct MallocArrayBuilderPolicy(size_t InitialItemCount) 73 | { 74 | static import mar.mem; 75 | 76 | /** Returns: null if out of memory */ 77 | static T[] increaseBuffer(T)(T[] buffer, size_t minNewLength, size_t preserveLength) 78 | { 79 | pragma(inline, true); 80 | import mar.conv : staticCast; 81 | auto result = increaseBuffer(buffer, minNewLength, preserveLength, T.sizeof); 82 | return staticCast!(T[])(result); 83 | } 84 | /// ditto 85 | static void[] increaseBuffer(void[] buffer, size_t minNewLength, size_t preserveLength, size_t elementSize) 86 | { 87 | import mar.array : acopy; 88 | 89 | size_t newLength; 90 | if (buffer.length == 0) 91 | newLength = InitialItemCount; 92 | else 93 | newLength = buffer.length * 2; 94 | if (newLength < minNewLength) 95 | newLength = minNewLength; 96 | 97 | auto newByteSize = newLength * elementSize; 98 | 99 | void* newBuffer; 100 | if (mar.mem.tryRealloc(buffer.ptr, newByteSize)) 101 | newBuffer = buffer.ptr; 102 | else 103 | { 104 | newBuffer = mar.mem.malloc(newByteSize); 105 | if (!newBuffer) 106 | return null; 107 | acopy(newBuffer, buffer.ptr, preserveLength * elementSize); 108 | mar.mem.free(buffer.ptr); 109 | } 110 | return newBuffer[0 .. newLength]; 111 | } 112 | static void free(T)(T[] buffer) 113 | { 114 | pragma(inline, true); 115 | mar.mem.free(buffer.ptr); 116 | } 117 | } 118 | 119 | unittest 120 | { 121 | import mar.array : aequals; 122 | 123 | { 124 | auto builder = ArrayBuilder!int(); 125 | assert(builder.data.length == 0); 126 | assert(builder.tryPut(400).passed); 127 | assert(builder.data.length == 1); 128 | assert(builder.data[0] == 400); 129 | builder.free(); 130 | assert(builder.data is null); 131 | } 132 | { 133 | auto builder = ArrayBuilder!int(); 134 | assert(builder.data.length == 0); 135 | foreach (i; 0 .. 600) 136 | { 137 | assert(builder.tryPut(i).passed); 138 | assert(builder.data.length == i + 1); 139 | foreach (j; 0 .. i) 140 | { 141 | assert(builder.data[j] == j); 142 | } 143 | } 144 | builder.free(); 145 | assert(builder.data is null); 146 | } 147 | { 148 | auto builder = ArrayBuilder!char(); 149 | assert(builder.data.length == 0); 150 | assert(builder.tryPutRange("hello").passed); 151 | assert(aequals(builder.data, "hello")); 152 | assert(builder.tryPut('!').passed); 153 | assert(aequals(builder.data, "hello!")); 154 | builder.free(); 155 | assert(builder.data is null); 156 | } 157 | } -------------------------------------------------------------------------------- /src/mar/ascii.d: -------------------------------------------------------------------------------- 1 | module mar.ascii; 2 | 3 | bool isUnreadable(char c) pure nothrow @nogc @safe 4 | { 5 | return c < ' ' || (c > '~' && c <= 255); 6 | } 7 | auto printUnreadable(P)(P printer, char c) 8 | { 9 | import mar.print : hexTableUpper; 10 | 11 | if (c == '\n') return printer.put("\\n"); 12 | if (c == '\t') return printer.put("\\t"); 13 | if (c == '\r') return printer.put("\\r"); 14 | if (c == '\0') return printer.put("\\0"); 15 | char[4] str; 16 | str[0] = '\\'; 17 | str[1] = 'x'; 18 | str[2] = hexTableUpper[c >> 4]; 19 | str[3] = hexTableUpper[c & 0xF]; 20 | return printer.put(str); 21 | } 22 | auto printEscape(P)(P printer, char c) 23 | { 24 | pragma(inline, true); 25 | return isUnreadable(c) ? 26 | printUnreadable(printer, c) : 27 | printer.putc(c); 28 | } 29 | auto printEscape(P)(P printer, const(char)* ptr, const char *limit) 30 | { 31 | auto flushed = ptr; 32 | for(;; ptr++) 33 | { 34 | char c; 35 | if (ptr >= limit || isUnreadable(c = ptr[0])) 36 | { 37 | { 38 | auto result = printer.put(flushed[0 .. ptr - flushed]); 39 | if (ptr >= limit || result.failed) 40 | return result; 41 | } 42 | { 43 | auto result = printUnreadable(printer, c); 44 | if (result.failed) 45 | return result; 46 | } 47 | flushed = ptr + 1; 48 | } 49 | } 50 | } 51 | 52 | auto formatEscape(char c) 53 | { 54 | static struct Print 55 | { 56 | char c; 57 | auto print(P)(P printer) const 58 | { 59 | return printEscape(printer, c); 60 | } 61 | } 62 | return Print(c); 63 | } 64 | auto formatEscape(const(char)[] str) 65 | { 66 | pragma(inline, true); 67 | return formatEscape(str.ptr, str.ptr + str.length); 68 | } 69 | auto formatEscape(const(char)* ptr, const char* limit) 70 | { 71 | static struct Print 72 | { 73 | const(char)* ptr; 74 | const(char)* limit; 75 | auto print(P)(P printer) const 76 | { 77 | return printEscape(printer, ptr, limit); 78 | } 79 | } 80 | return Print(ptr, limit); 81 | } 82 | 83 | unittest 84 | { 85 | import mar.print : testFormattedValue; 86 | testFormattedValue(`a`, 'a'.formatEscape); 87 | testFormattedValue(`\n`, '\n'.formatEscape); 88 | testFormattedValue(`\0`, '\0'.formatEscape); 89 | testFormattedValue(`\t`, '\t'.formatEscape); 90 | 91 | testFormattedValue(``, "".formatEscape); 92 | testFormattedValue(`a`, "a".formatEscape); 93 | testFormattedValue(`foo`, "foo".formatEscape); 94 | testFormattedValue(`foo\n`, "foo\n".formatEscape); 95 | testFormattedValue(`\n\t\r\0`, "\n\t\r\0".formatEscape); 96 | } 97 | 98 | -------------------------------------------------------------------------------- /src/mar/bitarray.d: -------------------------------------------------------------------------------- 1 | module more.bitarray; 2 | 3 | struct BitArray(bool IsFixed) 4 | { 5 | enum BitsPerSizet = size_t.sizeof * 8; 6 | 7 | private ArrayBuilder!size_t array; 8 | private size_t bitLength; 9 | 10 | bool get(size_t index) const 11 | in { 12 | static if (IsFixed) 13 | assert(index < bitLength, "bit index out of bounds"); 14 | } do 15 | { 16 | auto arrayIndex = index / BitsPerSizet; 17 | static if (!IsFixed) 18 | { 19 | if (arrayIndex >= array.length) 20 | return false; 21 | } 22 | return 0 != (array[arrayIndex] & (cast(size_t)1 << (index % BitsPerSizet))); 23 | } 24 | /* 25 | void enable(size_t index) 26 | { 27 | 28 | } 29 | */ 30 | } 31 | alias FixedBitArray = BitArray!true; 32 | alias DynamicBitArray = BitArray!true; 33 | 34 | unittest 35 | { 36 | { 37 | auto ba = BitArray(); 38 | } 39 | } -------------------------------------------------------------------------------- /src/mar/c.d: -------------------------------------------------------------------------------- 1 | /** 2 | Contains code to help interfacing with C code. 3 | */ 4 | module mar.c; 5 | 6 | version (NoStdc) {} else 7 | { 8 | static import core.stdc.string; 9 | } 10 | 11 | import mar.sentinel : SentinelPtr, lit; 12 | 13 | // TODO: this definitions may change depending on the 14 | // platform. These types are meant to be used 15 | // when declaring extern (C) functions who return 16 | // types int/unsigned. 17 | alias cint = int; 18 | alias cuint = uint; 19 | 20 | /** 21 | A `cstring`` is pointer to an array of characters that is terminated 22 | with a '\0' (null) character. 23 | */ 24 | alias cstring = SentinelPtr!(const(char)); 25 | /// ditto 26 | alias cwstring = SentinelPtr!(const(wchar)); 27 | /// ditto 28 | alias cdstring = SentinelPtr!(const(dchar)); 29 | 30 | version(unittest) 31 | { 32 | // demonstrate that C functions can be redefined using SentinelPtr 33 | import mar.string : strlen; 34 | } 35 | 36 | unittest 37 | { 38 | assert(5 == strlen(lit!"hello".ptr)); 39 | 40 | // NEED MULTIPLE ALIAS THIS to allow SentinelArray to implicitly convert to SentinelPtr 41 | //assert(5 == strlen(lit!"hello")); 42 | 43 | // type of string literals should be changed to SentinelString in order for this to work 44 | //assert(5 == strlen("hello".ptr"); 45 | 46 | // this requires both conditions above to work 47 | //assert(5 == strlen("hello")); 48 | } 49 | 50 | unittest 51 | { 52 | import mar.sentinel; 53 | import mar.array; 54 | 55 | char[10] buffer = void; 56 | acopy(buffer.ptr, "hello"); 57 | buffer[5] = '\0'; 58 | SentinelArray!char hello = buffer[0..5].verifySentinel; 59 | assert(5 == strlen(hello.ptr)); 60 | } 61 | 62 | 63 | struct TempCStringNoOp 64 | { 65 | cstring str; 66 | } 67 | 68 | struct TempCString 69 | { 70 | import mar.typecons : unconst; 71 | import mar.array : acopy; 72 | import mar.mem : malloc, free; 73 | 74 | cstring str; 75 | private bool needToFree; 76 | this(const(char)[] str, void* allocaBuffer) 77 | { 78 | import mar.sentinel : assumeSentinel; 79 | if (allocaBuffer) 80 | this.str = (cast(char*)allocaBuffer).assumeSentinel; 81 | else 82 | { 83 | this.str = (cast(char*)malloc(str.length + 1)).assumeSentinel; 84 | assert(this.str, "malloc returned NULL"); 85 | this.needToFree = true; 86 | } 87 | acopy(this.str.raw, str); 88 | (cast(char*)this.str)[str.length] = '\0'; 89 | } 90 | ~this() 91 | { 92 | if (needToFree) 93 | { 94 | free(cast(void*)this.str.raw); 95 | needToFree = false; 96 | //this.str = null; 97 | } 98 | } 99 | } 100 | 101 | mixin template tempCString(string newVar, string stringVar, string maxAlloca = "200") 102 | { 103 | static if (is(typeof(mixin(stringVar ~ `.asCString`)))) 104 | { 105 | mixin(`auto ` ~ newVar ~ ` = TempCStringNoOp(` ~ stringVar ~ `.asCString);`); 106 | } 107 | else 108 | { 109 | version (NoStdc) 110 | { 111 | static assert(0, "alloca from druntime not working"); 112 | } 113 | import mar.c : TempCString; 114 | import mar.mem : alloca; 115 | mixin(`auto ` ~ newVar ~ ` = TempCString(` ~ stringVar ~ `, (` ~ stringVar ~ `.length <= ` ~ 116 | maxAlloca ~ `) ? alloca(` ~ stringVar ~ `.length + 1) : null);`); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/mar/cannotfail.d: -------------------------------------------------------------------------------- 1 | module mar.cannotfail; 2 | 3 | struct CannotFail 4 | { 5 | enum failed = false; 6 | //static bool failed() { pragma(inline, true); return false; } 7 | } 8 | -------------------------------------------------------------------------------- /src/mar/cmdopt.d: -------------------------------------------------------------------------------- 1 | module maros.cmdopt; 2 | 3 | import mar.c : cstring; 4 | 5 | /** 6 | Get the value of a command line option argument and increments 7 | the counter. 8 | On errors, prints a message to stderr and exits. 9 | */ 10 | auto getOptArg(T)(T args, uint* i) 11 | { 12 | import mar.enforce; 13 | import mar.stdio : stderr; 14 | 15 | (*i)++; 16 | static if (__traits(hasMember, args, "length")) 17 | { 18 | enforce((*i) < args.length, "Error: option '", args[(*i)-1], "' requires an argument"); 19 | } 20 | auto arg = args[*i]; 21 | static if (!__traits(hasMember, args, "length")) 22 | { 23 | enforce(arg != arg.nullValue, "Error: option '", args[(*i)-1], "' requires an argument"); 24 | } 25 | return arg; 26 | } -------------------------------------------------------------------------------- /src/mar/conv.d: -------------------------------------------------------------------------------- 1 | module mar.conv; 2 | 3 | import mar.passfail; 4 | import mar.flag; 5 | import mar.traits : isArithmetic; 6 | import mar.typecons : Nullable, nullable; 7 | 8 | auto staticCast(T, U)(auto ref U value) 9 | { 10 | pragma(inline, true); 11 | return *cast(T*)&value; 12 | } 13 | 14 | string asString(Enum)(Enum enumValue, string missingValue = null) if ( is(Enum == enum) ) 15 | { 16 | switch (enumValue) 17 | { 18 | foreach (member; __traits(allMembers, Enum)) 19 | { 20 | case __traits(getMember, Enum, member): return member; 21 | } 22 | default: return missingValue; 23 | } 24 | } 25 | 26 | Nullable!Enum tryParseEnum(Enum)(const(char)[] str) 27 | { 28 | foreach (member; __traits(allMembers, Enum)) 29 | { 30 | if (str == member) 31 | return nullable(__traits(getMember, Enum, member)); 32 | } 33 | return Nullable!Enum.init; 34 | } 35 | 36 | 37 | passfail tryTo(To,From)(From from, To* to) 38 | { 39 | static if (is(From == string)) 40 | { 41 | static if (isArithmetic!To) 42 | { 43 | return charRangeToArithmetic!To(from, to); 44 | } 45 | else static assert(0, "tryTo From=" ~ From.stringof ~ " To=" ~ To.stringof ~ " not implemented"); 46 | } 47 | else static if (__traits(hasMember, From, "SentinelPtrFor") && 48 | is (From.SentinelPtrFor == char)) 49 | { 50 | static if (isArithmetic!To) 51 | { 52 | return charRangeToArithmetic!To(from.rangeCopy, to); 53 | } 54 | else static assert(0, "tryTo From=" ~ From.stringof ~ " To=" ~ To.stringof ~ " not implemented"); 55 | } 56 | else static assert(0, "tryTo From=" ~ From.stringof ~ " not implemented"); 57 | } 58 | 59 | passfail charRangeToArithmetic(T, U)(U charRange, T* outValue) 60 | { 61 | if (charRange.empty) 62 | return passfail.fail; 63 | 64 | T val = void; 65 | { 66 | auto next = charRange.front; 67 | if (next > '9' || next < '0') 68 | return passfail.fail; 69 | val = cast(T)(next - '0'); 70 | } 71 | for (;;) 72 | { 73 | charRange.popFront(); 74 | if (charRange.empty) 75 | { 76 | *outValue = val; 77 | return passfail.pass; 78 | } 79 | auto next = charRange.front; 80 | if (next > '9' || next < '0') 81 | return passfail.fail; 82 | val *= 10; 83 | val += cast(T)(next - '0'); 84 | } 85 | } 86 | 87 | 88 | template to(T) 89 | { 90 | T to(A...)(A args) { return toImpl!T(args); } 91 | } 92 | 93 | private T toImpl(T, S)(S value) 94 | { 95 | // TODO: support char[] as well 96 | static if (is(T == string)) 97 | { 98 | static assert(0, "not implemented"); 99 | } 100 | else static assert(0, "to " ~ T.stringof ~ " not implemented"); 101 | } 102 | 103 | 104 | /+ 105 | Not sure these functions are necessary, since I have mar.print.sprint and family. 106 | All the logic to print a value is in that module, having that duplicated in this 107 | API seems to just add complexity. 108 | 109 | /*************************************************************** 110 | * Convenience functions for converting one or more arguments 111 | * of any type into _text (the three character widths). 112 | */ 113 | string text(T...)(T args) 114 | if (T.length > 0) { return textImpl!string(args); } 115 | 116 | ///ditto 117 | wstring wtext(T...)(T args) 118 | if (T.length > 0) { return textImpl!wstring(args); } 119 | 120 | ///ditto 121 | dstring dtext(T...)(T args) 122 | if (T.length > 0) { return textImpl!dstring(args); } 123 | 124 | private S textImpl(S, U...)(U args) 125 | { 126 | static if (U.length == 0) 127 | { 128 | return null; 129 | } 130 | else static if (U.length == 1) 131 | { 132 | return to!S(args[0]); 133 | } 134 | else 135 | { 136 | static if (is(T == string)) 137 | { 138 | import mar.print : sprintMalloc; 139 | return sprintMallocNoSentinel(args); 140 | } 141 | } 142 | } 143 | +/ -------------------------------------------------------------------------------- /src/mar/ctypes.d: -------------------------------------------------------------------------------- 1 | module mar.ctypes; 2 | 3 | version (linux) 4 | { 5 | public import mar.linux.cthunk; 6 | } 7 | else version (Windows) 8 | { 9 | alias off_t = uint; 10 | } 11 | else static assert(0, "unsupported platform"); -------------------------------------------------------------------------------- /src/mar/disk/mbr.d: -------------------------------------------------------------------------------- 1 | module mar.disk.mbr; 2 | 3 | import mar.endian : LittleEndianOf, BigEndianOf, toBigEndian; 4 | 5 | /** 6 | Implements an API to manage disks using the MBR partition format. 7 | */ 8 | 9 | struct ChsAddress 10 | { 11 | ubyte[3] value; 12 | void setDefault() 13 | { 14 | value[] = 0; 15 | } 16 | } 17 | 18 | struct PartitionType 19 | { 20 | enum Value : ubyte 21 | { 22 | empty = 0x00, 23 | linuxSwapOrSunContainer = 0x82, 24 | linux = 0x83, 25 | } 26 | private Value value; 27 | Value enumValue() const { return value; } 28 | string name() const 29 | { 30 | import mar.conv : asString; 31 | return asString(value, "?"); 32 | } 33 | template opDispatch(string name) 34 | { 35 | enum opDispatch = PartitionType(__traits(getMember, Value, name)); 36 | } 37 | } 38 | 39 | enum PartitionStatus : ubyte 40 | { 41 | none, 42 | bootable = 0x80, 43 | } 44 | 45 | struct PartitionOnDiskFormat 46 | { 47 | align(1): 48 | PartitionStatus status; 49 | ChsAddress firstSectorChs; 50 | PartitionType type; 51 | ChsAddress lastSectorChs; 52 | LittleEndianOf!uint firstSectorLba; 53 | LittleEndianOf!uint sectorCount; 54 | 55 | bool bootable() const { return status == 0x80; } 56 | } 57 | static assert(PartitionOnDiskFormat.sizeof == 16); 58 | 59 | enum BootstrapSize = 446; 60 | 61 | struct OnDiskFormat 62 | { 63 | align(1): 64 | ubyte[BootstrapSize] bootstrap; 65 | PartitionOnDiskFormat[4] partitions; 66 | ubyte[2] bootSignature; 67 | 68 | void setBootSignature() 69 | { 70 | *(cast(ushort*)bootSignature.ptr) = toBigEndian!ushort(0x55AA).getRawValue; 71 | } 72 | 73 | ubyte[512]* bytes() const { return cast(ubyte[512]*)&this; } 74 | BigEndianOf!ushort bootSignatureValue() const 75 | { 76 | return BigEndianOf!ushort(*(cast(ushort*)bootSignature.ptr)); 77 | } 78 | bool signatureIsValid() const 79 | { 80 | return bootSignatureValue() == toBigEndian!ushort(0x55AA); 81 | } 82 | } 83 | static assert(OnDiskFormat.sizeof == 512); 84 | -------------------------------------------------------------------------------- /src/mar/endian.d: -------------------------------------------------------------------------------- 1 | module mar.endian; 2 | 3 | import mar.flag; 4 | 5 | version (BigEndian) 6 | { 7 | alias BigEndianOf(T) = EndianOf!(T, No.flip); 8 | alias LittleEndianOf(T) = EndianOf!(T, Yes.flip); 9 | } 10 | else // LittleEndian 11 | { 12 | alias BigEndianOf(T) = EndianOf!(T, Yes.flip); 13 | alias LittleEndianOf(T) = EndianOf!(T, No.flip); 14 | } 15 | 16 | struct EndianOf(T, Flag!"flip" flip) 17 | { 18 | union 19 | { 20 | private ubyte[T.sizeof] bytes; 21 | private T value; 22 | } 23 | this(T value) { this.value = value; } 24 | 25 | T getRawValue() const { return value; } 26 | 27 | T toHostEndian() const 28 | { 29 | pragma(inline, true); 30 | static if (flip) 31 | return cast(T)swapEndian(value); 32 | else 33 | return cast(T)value; 34 | } 35 | 36 | import mar.wrap; 37 | mixin WrapperFor!("value"); 38 | mixin WrapOpEquals!(No.includeWrappedType); 39 | // Note: do not use OpCmpIntegral because it assumes 40 | // the values are in native endian. 41 | // I could use it if I wrap the toHostEndian field 42 | // instead of "value" though. 43 | } 44 | 45 | BigEndianOf!T toBigEndian(T)(T value) pure nothrow @nogc 46 | { 47 | pragma(inline, true); 48 | version (BigEndian) 49 | return BigEndianOf!T(value); 50 | else 51 | return BigEndianOf!T(swapEndian(value)); 52 | } 53 | LittleEndianOf!T toLittleEndian(T)(T value) pure nothrow @nogc 54 | { 55 | pragma(inline, true); 56 | version (LittleEndian) 57 | return LittleEndianOf!T(value); 58 | else 59 | return LittleEndianOf!T(swapEndian(value)); 60 | } 61 | 62 | ushort swapEndian(ushort val) pure nothrow @nogc 63 | { 64 | return ((val & 0xff00U) >> 8) | 65 | ((val & 0x00ffU) << 8); 66 | } 67 | uint swapEndian(uint val) pure nothrow @nogc 68 | { 69 | return ((val >> 24U) & 0x000000ff) | 70 | ((val << 8U) & 0x00ff0000) | 71 | ((val >> 8U) & 0x0000ff00) | 72 | ((val << 24U) & 0xff000000); 73 | } -------------------------------------------------------------------------------- /src/mar/enforce.d: -------------------------------------------------------------------------------- 1 | module mar.enforce; 2 | 3 | import mar.passfail; 4 | 5 | template ResultExpression(string Mixin) 6 | { 7 | private struct Value 8 | { 9 | enum ResultExpressionMixin = Mixin; 10 | } 11 | enum ResultExpression = Value(); 12 | } 13 | 14 | /** 15 | Use to represent the result in an enforce error message. 16 | 17 | Example: 18 | --- 19 | result.enforce("this result failed: it is ", Result.val); 20 | result.enforce("this result failed: it is ", Result.errorCode.subField); 21 | --- 22 | */ 23 | struct Result 24 | { 25 | template opDispatch(string name) 26 | { 27 | static if (name == "val") 28 | enum opDispatch = ResultExpression!"result"; 29 | else 30 | enum opDispatch = ResultExpression!("result." ~ name); 31 | } 32 | } 33 | 34 | // TODO: probably move this 35 | private template Aliases(T...) 36 | { 37 | alias Aliases = T; 38 | } 39 | // Create a tuple of aliases that does not expand 40 | private struct SubAliases(T...) 41 | { 42 | alias Expand = T; 43 | } 44 | 45 | // TODO: is it faster to process args forwards or backwards? 46 | private template Replace(size_t Index, alias With, Types, Values...) 47 | { 48 | static if (Index == Values.length) 49 | { 50 | alias Replace = Values[$ .. $]; 51 | } 52 | else static if (is(typeof(Types.Expand[Index].ResultExpressionMixin))) 53 | { 54 | alias result = With; // result is used in the expression mixin 55 | alias Replace = Aliases!(mixin(Types.Expand[Index].ResultExpressionMixin), 56 | Replace!(Index + 1, With, Types, Values)); 57 | } 58 | else 59 | { 60 | alias Replace = Aliases!(Values[Index], 61 | Replace!(Index + 1, With, Types, Values)); 62 | } 63 | } 64 | 65 | version (D_BetterC) { } else 66 | { 67 | /** 68 | Thrown when an enforce fails. Note that an enforce statement will have already printed 69 | an error to stderr, so the error does not need to be printed in the catch clause. 70 | */ 71 | class EnforceException : Exception 72 | { 73 | this() 74 | { 75 | super("an enforce failed, see error above"); 76 | } 77 | } 78 | } 79 | 80 | void enforce(E...)(bool cond, E errorMsgValues) 81 | { 82 | if (!cond) 83 | { 84 | import mar.stdio : stderr; 85 | static if (E.length == 0) 86 | stderr.writeln("An error occurred"); 87 | else 88 | stderr.writeln("Error: ", errorMsgValues); 89 | version (D_BetterC) 90 | { 91 | import mar.process : exit; 92 | exit(1); 93 | } 94 | else 95 | { 96 | throw new EnforceException(); 97 | } 98 | } 99 | } 100 | auto enforce(T, E...)(T result, E errorMsgValues) if (__traits(hasMember, T, "failed")) 101 | { 102 | if (result.failed) 103 | { 104 | import mar.stdio : stderr; 105 | static if (E.length == 0) 106 | stderr.writeln("An error occurred"); 107 | else 108 | stderr.writeln("Error: ", Replace!(0, result, SubAliases!E, errorMsgValues)); 109 | version (D_BetterC) 110 | { 111 | import mar.process : exit; 112 | exit(1); 113 | } 114 | else 115 | { 116 | throw new EnforceException(); 117 | } 118 | } 119 | static if (__traits(hasMember, T, "val")) 120 | return result.val; 121 | } 122 | 123 | passfail reportFail(E...)(bool cond, E errorMsgValues) 124 | { 125 | if (!cond) 126 | { 127 | import mar.stdio : stderr; 128 | static if (E.length == 0) 129 | stderr.writeln("An error occurred"); 130 | else 131 | stderr.writeln("Error: ", errorMsgValues); 132 | return passfail.fail; 133 | } 134 | return passfail.pass; 135 | } 136 | passfail reportFail(T, E...)(T result, E errorMsgValues) if (__traits(hasMember, T, "failed")) 137 | { 138 | if (result.failed) 139 | { 140 | import mar.stdio : stderr; 141 | static if (E.length == 0) 142 | stderr.writeln("An error occurred"); 143 | else 144 | stderr.writeln("Error: ", Replace!(0, result, SubAliases!E, errorMsgValues)); 145 | return passfail.fail; 146 | } 147 | return passfail.pass; 148 | } 149 | 150 | unittest 151 | { 152 | import mar.expect; 153 | { 154 | enforce(passfail.pass); 155 | if (false) 156 | enforce(passfail.fail); 157 | assert(!reportFail(passfail.pass).failed); 158 | assert(reportFail(passfail.fail).failed); 159 | } 160 | { 161 | enforce(MemoryResult.success); 162 | if (false) 163 | enforce(MemoryResult.outOfMemory); 164 | assert(!reportFail(MemoryResult.success).failed); 165 | assert(reportFail(MemoryResult.outOfMemory).failed); 166 | } 167 | { 168 | enforce(passfail.pass, "something failed!"); 169 | if (false) 170 | enforce(passfail.fail, "something failed!"); 171 | assert(!reportFail(passfail.pass, "something failed!").failed); 172 | assert(reportFail(passfail.fail, "something failed!").failed); 173 | } 174 | { 175 | enforce(MemoryResult.success, Result.val); 176 | if (false) 177 | enforce(MemoryResult.outOfMemory, Result.val); 178 | assert(!reportFail(MemoryResult.success, Result.val).failed); 179 | assert(reportFail(MemoryResult.outOfMemory, Result.val).failed); 180 | } 181 | { 182 | enforce(MemoryResult.success, "and the error is...", Result.val); 183 | if (false) 184 | enforce(MemoryResult.outOfMemory, "and the error is...", Result.val); 185 | assert(!reportFail(MemoryResult.success, "and the error is...", Result.val).failed); 186 | assert(reportFail(MemoryResult.outOfMemory, "and the error is...", Result.val).failed); 187 | } 188 | } -------------------------------------------------------------------------------- /src/mar/env.d: -------------------------------------------------------------------------------- 1 | module mar.env; 2 | 3 | import mar.sentinel : SentinelPtr, assumeSentinel; 4 | import mar.c : cstring; 5 | 6 | cstring getenv(SentinelPtr!cstring envp, const(char)[] name) 7 | { 8 | if (envp) 9 | { 10 | LvarLoop: 11 | for (;; envp++) 12 | { 13 | auto env = envp[0]; 14 | if (!env) 15 | break; 16 | size_t i = 0; 17 | for (; i < name.length; i++) 18 | { 19 | if (env[i] != name[i]) 20 | continue LvarLoop; 21 | } 22 | if (env[i] == '=') 23 | return (&env[i + 1]).assumeSentinel; 24 | } 25 | } 26 | return cstring.nullValue; 27 | } -------------------------------------------------------------------------------- /src/mar/file/package.d: -------------------------------------------------------------------------------- 1 | module mar.file; 2 | 3 | import mar.c : cstring; 4 | import mar.expect; 5 | 6 | version (linux) 7 | { 8 | public import mar.linux.file; 9 | } 10 | else version (Windows) 11 | { 12 | public import mar.windows.file; 13 | import mar.windows : Handle; 14 | import mar.windows.kernel32 : CloseHandle; 15 | // dummy mode flags for windows 16 | enum ModeFlags 17 | { 18 | execOther = 0, 19 | writeOther = 0, 20 | readOther = 0, 21 | execGroup = 0, 22 | writeGroup = 0, 23 | readGroup = 0, 24 | execUser = 0, 25 | writeUser = 0, 26 | readUser = 0, 27 | isFifo = 0, 28 | isCharDev = 0, 29 | isDir = 0, 30 | isRegFile = 0, 31 | } 32 | } 33 | else static assert(0, __MODULE__ ~ " is not supported on this platform"); 34 | 35 | static struct ModeSet 36 | { 37 | enum rwxOther = ModeFlags.readOther | ModeFlags.writeOther | ModeFlags.execOther; 38 | enum rwxGroup = ModeFlags.readGroup | ModeFlags.writeGroup | ModeFlags.execGroup; 39 | enum rwxUser = ModeFlags.readUser | ModeFlags.writeUser | ModeFlags.execUser; 40 | 41 | enum rwOther = ModeFlags.readOther | ModeFlags.writeOther; 42 | enum rwGroup = ModeFlags.readGroup | ModeFlags.writeGroup; 43 | enum rwUser = ModeFlags.readUser | ModeFlags.writeUser ; 44 | } 45 | 46 | /** 47 | Platform independent open file configuration 48 | */ 49 | struct OpenFileOpt 50 | { 51 | version (linux) 52 | { 53 | import mar.linux.cthunk : mode_t; 54 | import mar.linux.file.perm : ModeFlags; 55 | 56 | private OpenFlags flags; 57 | private mode_t _mode; 58 | } 59 | else version (Windows) 60 | { 61 | private OpenAccess access; 62 | private FileShareMode shareMode; 63 | private FileCreateMode createMode; 64 | } 65 | 66 | this(OpenAccess access) 67 | { 68 | version (linux) 69 | { 70 | this.flags = OpenFlags(access); 71 | } 72 | else version (Windows) 73 | { 74 | this.access = access; 75 | this.createMode = FileCreateMode.openExisting; 76 | } 77 | else static assert(0, __MODULE__ ~ " is not supported on this platform"); 78 | } 79 | 80 | /// create a new file if it doesn't exist, otherwise, fail 81 | auto ref createOnly() 82 | { 83 | pragma(inline, true); 84 | version (linux) 85 | this.flags.val |= OpenCreateFlags.creat | OpenCreateFlags.excl; 86 | else version (Windows) 87 | this.createMode = FileCreateMode.createNew; 88 | return this; 89 | } 90 | // create a new file, truncate it if it already exists 91 | auto ref createOrTruncate() 92 | { 93 | pragma(inline, true); 94 | version (linux) 95 | this.flags.val |= OpenCreateFlags.creat | OpenCreateFlags.trunc; 96 | else version (Windows) 97 | this.createMode = FileCreateMode.createAlways; 98 | return this; 99 | } 100 | // create a new file or open the existing file 101 | auto ref createOrOpen() 102 | { 103 | pragma(inline, true); 104 | version (linux) 105 | this.flags.val |= OpenCreateFlags.creat; 106 | else version (Windows) 107 | this.createMode = FileCreateMode.openExisting; 108 | return this; 109 | } 110 | 111 | auto ref shareDelete() 112 | { 113 | pragma(inline, true); 114 | version (Windows) 115 | shareMode |= FileShareMode.delete_; 116 | return this; 117 | } 118 | auto ref shareRead() 119 | { 120 | pragma(inline, true); 121 | version (Windows) 122 | shareMode |= FileShareMode.read; 123 | return this; 124 | } 125 | auto ref shareWrite() 126 | { 127 | pragma(inline, true); 128 | version (Windows) 129 | shareMode |= FileShareMode.write; 130 | return this; 131 | } 132 | 133 | auto ref mode(ModeFlags modeFlags) 134 | { 135 | pragma(inline, true); 136 | version (linux) 137 | this._mode = modeFlags; 138 | // ignore on windows 139 | return this; 140 | } 141 | } 142 | 143 | /** 144 | Platform independent open file function 145 | */ 146 | FileD tryOpenFile(cstring filename, OpenFileOpt opt) 147 | { 148 | version (linux) 149 | { 150 | return open(filename, opt.flags, opt._mode); 151 | } 152 | else version (Windows) 153 | { 154 | import mar.windows.kernel32 : CreateFileA; 155 | 156 | return CreateFileA(filename, opt.access, opt.shareMode, null, opt.createMode, 0, Handle.nullValue); 157 | } 158 | else static assert(0, __FUNCTION__ ~ " is not supported on this platform"); 159 | } 160 | 161 | /+ 162 | version (linux) 163 | alias closeFile = close; 164 | else version (Windows) 165 | 166 | { 167 | alias closeFile = CloseHandle; 168 | auto closeFile(FileD) 169 | } 170 | +/ 171 | version (Windows) 172 | { 173 | // Use contiguous values 174 | enum MMapAccess : uint 175 | { 176 | readOnly = 0, 177 | readWrite = 1, 178 | readWriteCopy = 2, 179 | readExecute = 3, 180 | /* 181 | 182 | readOnly = 2, // PAGE_READONLY 183 | readWrite = 4, // PAGE_READWRITE 184 | readWriteCopy = 8, // PAGE_WRITECOPY 185 | //readExecute 186 | */ 187 | } 188 | immutable uint[MMapAccess.max+1] mmapAccessToFileMapping = [ 189 | MMapAccess.readOnly : 2, // PAGE_READONLY 190 | MMapAccess.readWrite : 4, // PAGE_READWRITE 191 | MMapAccess.readWriteCopy : 8, // PAGE_WRITECOPY 192 | MMapAccess.readExecute : 32, // PAGE_EXECUTE_READ 193 | ]; 194 | private enum FILE_MAP_COPY = 0x01; 195 | private enum FILE_MAP_WRITE = 0x02; 196 | private enum FILE_MAP_READ = 0x04; 197 | private enum FILE_MAP_EXECUTE = 0x20; 198 | immutable uint[MMapAccess.max+1] mmapAccessToMapViewOfFile = [ 199 | MMapAccess.readOnly : FILE_MAP_READ, 200 | MMapAccess.readWrite : FILE_MAP_WRITE, 201 | MMapAccess.readWriteCopy : FILE_MAP_COPY, 202 | MMapAccess.readExecute : FILE_MAP_EXECUTE, 203 | ]; 204 | } 205 | else 206 | { 207 | enum MMapAccess 208 | { 209 | // placeholders 210 | readOnly = 0, 211 | readWrite = 0, 212 | readWriteCopy = 0, 213 | } 214 | } 215 | 216 | 217 | version (Windows) 218 | { 219 | struct MappedFile 220 | { 221 | private Handle fileMapping; 222 | ubyte[] mem; 223 | mixin ExpectMixin!("CloseResult", void, 224 | ErrorCase!("unmapViewOfFileFailed", "UnmapViewOfFile failed, error=%", uint), 225 | ErrorCase!("closeFileMappingFailed", "CloseHandle on file mapping failed, error=%", uint)); 226 | CloseResult close() 227 | { 228 | import mar.windows.kernel32 : GetLastError, UnmapViewOfFile, CloseHandle; 229 | if (UnmapViewOfFile(mem.ptr).failed) 230 | return CloseResult.unmapViewOfFileFailed(GetLastError()); 231 | if (CloseHandle(fileMapping).failed) 232 | return CloseResult.closeFileMappingFailed(GetLastError()); 233 | return CloseResult.success(); 234 | } 235 | } 236 | mixin ExpectMixin!("MMapResult", MappedFile, 237 | ErrorCase!("getFileSizeFailed", "GetFileSize failed, error=%", uint), 238 | ErrorCase!("fileTooBig", "The file is too big to be mapped in one buffer"), 239 | ErrorCase!("createFileMappingFailed", "CreateFileMapping failed, error=%", uint), 240 | ErrorCase!("mapViewOfFileFailed", "MapViewOfFile failed, error=%", uint)); 241 | // TODO: move this function somewhere else 242 | uint high32(T)(T value) 243 | { 244 | pragma(inline, true); 245 | static if (T.sizeof >=8) 246 | return cast(uint)(value >> 32); 247 | return 0; 248 | } 249 | } 250 | auto mmap(FileD file, MMapAccess access, ulong offset, size_t size) 251 | { 252 | version (Windows) 253 | { 254 | import mar.windows : INVALID_FILE_SIZE; 255 | import mar.windows.kernel32 : GetLastError, 256 | GetFileSize, CreateFileMappingA, MapViewOfFile; 257 | 258 | if (size == 0) 259 | { 260 | uint high = 0; 261 | auto low = GetFileSize(file.asHandle, &high); 262 | if (low == INVALID_FILE_SIZE) 263 | return MMapResult.getFileSizeFailed(GetLastError()); 264 | ulong total = ((cast(ulong)high) << 32) | cast(ulong)low; 265 | static if (ulong.sizeof > size_t.sizeof) 266 | { 267 | if (total > size_t.max) 268 | return MMapResult.fileTooBig(); 269 | } 270 | size = cast(size_t)total; 271 | } 272 | 273 | auto fileMapping = CreateFileMappingA(file.asHandle, null, mmapAccessToFileMapping[access], 274 | high32(size), cast(uint)size, cstring.nullValue); 275 | if (fileMapping.isNull) 276 | return MMapResult.createFileMappingFailed(GetLastError()); 277 | auto ptr = MapViewOfFile(fileMapping, mmapAccessToMapViewOfFile[access], 278 | high32(offset), cast(uint)offset, size); 279 | if (ptr is null) 280 | return MMapResult.mapViewOfFileFailed(GetLastError()); 281 | return MMapResult.success(MappedFile(fileMapping, (cast(ubyte*)ptr)[0 .. size])); 282 | } 283 | else 284 | { 285 | assert(0, "mmap not impl here"); 286 | } 287 | } -------------------------------------------------------------------------------- /src/mar/file/perm.d: -------------------------------------------------------------------------------- 1 | module mar.file.perm; 2 | 3 | version (linux) 4 | { 5 | public import mar.linux.file.perm; 6 | } 7 | else version (Windows) 8 | { 9 | public import mar.windows.file.perm; 10 | } 11 | else static assert(0, __MODULE__ ~ " is not supported on this platform"); 12 | -------------------------------------------------------------------------------- /src/mar/filesys.d: -------------------------------------------------------------------------------- 1 | module mar.filesys; 2 | 3 | version (linux) 4 | { 5 | public import mar.linux.filesys; 6 | static import mar.linux.filesys; 7 | } 8 | else version (Windows) 9 | { 10 | public import mar.windows.filesys; 11 | static import mar.windows.filesys; 12 | } 13 | else static assert(0, __MODULE__ ~ " is not supported on this platform"); 14 | 15 | import mar.c : cstring; 16 | 17 | struct MkdirConfig 18 | { 19 | version (linux) 20 | { 21 | import mar.linux.cthunk : mode_t; 22 | 23 | private mode_t mode; 24 | void setPosixMode(mode_t mode) 25 | { 26 | this.mode = mode; 27 | } 28 | mode_t getPosixMode() const 29 | { 30 | return mode; 31 | } 32 | } 33 | } 34 | auto mkdir(cstring pathname, MkdirConfig config) 35 | { 36 | version (linux) 37 | { 38 | return mar.linux.filesys.mkdir(pathname, config.getPosixMode); 39 | } 40 | else version (Windows) 41 | { 42 | import mar.windows.kernel32 : CreateDirectoryA; 43 | return CreateDirectoryA(pathname, null); 44 | } 45 | else static assert(0, __FUNCTION__ ~ " is not supported on this platform"); 46 | } 47 | -------------------------------------------------------------------------------- /src/mar/findprog.d: -------------------------------------------------------------------------------- 1 | module mar.findprog; 2 | 3 | import mar.sentinel : litPtr, assumeSentinel; 4 | import mar.c : cstring; 5 | import mar.file : fileExists; 6 | 7 | bool usePath(T)(T program) 8 | { 9 | import mar.array : indexOrMax; 10 | auto slashIndex = program.indexOrMax('/'); 11 | return slashIndex == slashIndex.max; 12 | } 13 | 14 | cstring findProgram(cstring pathEnv, const(char)[] program) 15 | { 16 | return findProgramIn(PathIterator(pathEnv), program); 17 | } 18 | cstring findProgramIn(T)(T pathRange, const(char)[] program) 19 | { 20 | foreach (path; pathRange) 21 | { 22 | auto result = getFileIfExists(path, program); 23 | if (!result.isNull) 24 | return result; 25 | } 26 | return cstring.nullValue; 27 | } 28 | 29 | // We make a separate function so we can alloca the function 30 | cstring getFileIfExists(const(char)[] dir, const(char)[] basename) 31 | { 32 | // TODO: use alloca? 33 | import mar.mem : free; 34 | import mar.print : sprintMallocSentinel; 35 | auto temp = sprintMallocSentinel(dir, "/", basename); 36 | //import mar.file; stdout.write("[DEBUG] checking '", temp, "'...\n"); 37 | if (fileExists(temp.ptr)) 38 | return temp.ptr; 39 | 40 | free(temp.ptr.raw); 41 | return cstring.nullValue; 42 | } 43 | 44 | struct PathIterator 45 | { 46 | const(char)[] current; 47 | this(cstring pathEnv) 48 | { 49 | this.current = pathEnv[0 .. 0]; 50 | popFront(); 51 | } 52 | bool empty() const { return current.ptr == null; } 53 | auto front() const { return current; } 54 | void popFront() 55 | { 56 | auto next = current.ptr + current.length; 57 | for (; next[0] == ':'; next++) 58 | { } 59 | 60 | if (next[0] == '\0') 61 | this.current = null; 62 | else 63 | { 64 | auto end = next + 1; 65 | for (;; end++) 66 | { 67 | auto c = end[0]; 68 | if (c == ':' || c == '\0') 69 | break; 70 | } 71 | this.current = next[0 .. end - next]; 72 | } 73 | } 74 | } 75 | 76 | unittest 77 | { 78 | // TODO: add unittests for PathIterator 79 | } -------------------------------------------------------------------------------- /src/mar/flag.d: -------------------------------------------------------------------------------- 1 | module mar.flag; 2 | 3 | template Flag(string name) 4 | { 5 | enum Flag : bool 6 | { 7 | no = false, yes = true 8 | } 9 | } 10 | 11 | struct Yes 12 | { 13 | template opDispatch(string name) 14 | { 15 | enum opDispatch = Flag!name.yes; 16 | } 17 | } 18 | 19 | struct No 20 | { 21 | template opDispatch(string name) 22 | { 23 | enum opDispatch = Flag!name.no; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/mar/from.d: -------------------------------------------------------------------------------- 1 | module mar.from; 2 | 3 | template from(string moduleName) 4 | { 5 | mixin("import from = " ~ moduleName ~ ";"); 6 | } 7 | -------------------------------------------------------------------------------- /src/mar/input.d: -------------------------------------------------------------------------------- 1 | module mar.input; 2 | 3 | import mar.expect; 4 | static import mar.file; 5 | 6 | mixin ExpectMixin!("ReadResult", char[], 7 | ErrorCase!("outOfMemory", "out of memory"), 8 | ErrorCase!("readError", "read error %", ptrdiff_t), 9 | ErrorCase!("lineTooLong", "line is too long")); 10 | 11 | struct LineReader(Hooks) 12 | { 13 | static assert(Hooks.minRead > 0, "minRead must be > 0"); 14 | 15 | Hooks.Reader reader; 16 | char[] buffer; 17 | size_t lineStart; 18 | size_t dataLimit; 19 | 20 | mixin Hooks.ReaderMixinTemplate; 21 | 22 | // TODO: return expected!(size_t, error_code) 23 | /** returns: index of newline */ 24 | ReadResult readln() 25 | { 26 | import mar.array : amove; 27 | 28 | size_t searchStart = lineStart; 29 | for (;;) 30 | { 31 | // find the newline 32 | for (size_t i = searchStart; i < dataLimit; i++) 33 | { 34 | if (buffer[i] == '\n') 35 | { 36 | auto saveLineStart = lineStart; 37 | lineStart = i + 1; 38 | return ReadResult.success(buffer[saveLineStart .. i]); 39 | } 40 | } 41 | 42 | if (lineStart > 0) 43 | { 44 | auto dataSize = dataLimit - lineStart; 45 | if (dataSize > 0) 46 | amove(buffer.ptr, buffer.ptr + lineStart, dataSize); 47 | lineStart = 0; 48 | dataLimit = dataSize; 49 | } 50 | 51 | auto available = buffer.length - dataLimit; 52 | if (available < Hooks.minRead) 53 | { 54 | auto result = Hooks.increaseBuffer(buffer, dataLimit); 55 | if (result.failed) 56 | return result; 57 | assert(result.val.length > this.buffer.length, "code bug"); 58 | this.buffer = result.val; 59 | available = buffer.length - dataLimit; 60 | } 61 | 62 | auto lastReadLength = reader.read(buffer[dataLimit .. $]); 63 | if (lastReadLength <= 0) 64 | { 65 | if (lastReadLength < 0) 66 | return ReadResult.readError(lastReadLength); 67 | auto saveLineStart = lineStart; 68 | lineStart = dataLimit; 69 | return ReadResult.success(buffer[saveLineStart .. dataLimit]); 70 | } 71 | 72 | searchStart = dataLimit; 73 | dataLimit += lastReadLength; 74 | } 75 | } 76 | } 77 | 78 | ReadResult defaultMallocIncreaseBuffer(size_t initialLength = 1024)(char[] buffer, size_t dataLength) 79 | { 80 | import mar.array : acopy; 81 | import mar.mem : malloc, free; 82 | 83 | size_t newSize; 84 | if (buffer.length == 0) 85 | newSize = initialLength; 86 | else 87 | newSize = buffer.length * 2; 88 | 89 | auto newBuffer = cast(char*)malloc(newSize); 90 | if (!newBuffer) 91 | return ReadResult.outOfMemory; 92 | 93 | if (dataLength > 0) 94 | acopy(newBuffer, buffer[0 .. dataLength]); 95 | free(buffer.ptr); 96 | return ReadResult.success(newBuffer[0 .. newSize]); 97 | } 98 | 99 | struct DefaultFileLineReaderHooks 100 | { 101 | enum minRead = 1; 102 | static struct Reader 103 | { 104 | mar.file.FileD file; 105 | auto read(char[] buffer) 106 | { 107 | pragma(inline, true); 108 | return mar.file.read(file, buffer).numval; 109 | } 110 | } 111 | mixin template ReaderMixinTemplate() 112 | { 113 | this(mar.file.FileD file) 114 | { 115 | this.reader = Hooks.Reader(file); 116 | } 117 | ~this() 118 | { 119 | import mar.mem : free; 120 | if (buffer.ptr) 121 | free(buffer.ptr); 122 | } 123 | } 124 | alias increaseBuffer = defaultMallocIncreaseBuffer; 125 | } 126 | 127 | 128 | // used for unittesting at the moment 129 | //extern (C) void main(int argc, void *argv, void* envp) 130 | unittest 131 | { 132 | { 133 | auto reader = LineReader!DefaultFileLineReaderHooks(mar.file.FileD(-1)); 134 | auto result = reader.readln(); 135 | assert(result.failed); 136 | assert(result.state == ReadResult.State.readError); 137 | } 138 | 139 | struct TestHooks 140 | { 141 | enum minRead = 1; 142 | static struct Reader 143 | { 144 | string str; 145 | size_t defaultReadSize; 146 | size_t nextOffset; 147 | auto read(char[] buffer) 148 | { 149 | auto readSize = defaultReadSize; 150 | 151 | if (nextOffset + readSize > str.length) 152 | readSize = str.length - nextOffset; 153 | 154 | if (readSize > buffer.length) 155 | readSize = buffer.length; 156 | 157 | auto save = nextOffset; 158 | nextOffset += readSize; 159 | import mar.array : acopy; 160 | acopy(buffer.ptr, str.ptr + save, readSize); 161 | return readSize; 162 | } 163 | } 164 | 165 | mixin template ReaderMixinTemplate() 166 | { 167 | this(string str, size_t readSize) 168 | { 169 | this.reader = Hooks.Reader(str, readSize); 170 | } 171 | ~this() 172 | { 173 | import mar.mem : free; 174 | if (buffer.ptr) 175 | free(buffer.ptr); 176 | } 177 | } 178 | alias increaseBuffer = defaultMallocIncreaseBuffer!1; 179 | } 180 | 181 | { 182 | auto reader = LineReader!TestHooks("", 1); 183 | auto result = reader.readln(); 184 | assert(!result.failed); 185 | assert(result.val.length == 0); 186 | } 187 | 188 | static struct TestCase 189 | { 190 | string input; 191 | string[] lines; 192 | } 193 | 194 | enum longString = 195 | "12345678901234567890123456789012345678901234567890" ~ 196 | "12345678901234567890123456789012345678901234567890" ~ 197 | "12345678901234567890123456789012345678901234567890" ~ 198 | "12345678901234567890123456789012345678901234567890" ~ 199 | "12345678901234567890123456789012345678901234567890" ~ 200 | "12345678901234567890123456789012345678901234567890" ~ 201 | "12345678901234567890123456789012345678901234567890" ~ 202 | "12345678901234567890123456789012345678901234567890"; 203 | enum longString2 = longString ~ longString ~ longString ~ longString; 204 | static immutable TestCase[] testCases = [ 205 | TestCase("a", ["a"]), 206 | TestCase("a\n", ["a"]), 207 | TestCase("a\nb", ["a", "b"]), 208 | TestCase("a\nb\n", ["a", "b"]), 209 | TestCase("a\nb\nc", ["a", "b", "c"]), 210 | TestCase("a\nb\nc\n", ["a", "b", "c"]), 211 | TestCase("foo", ["foo"]), 212 | TestCase("foo\n", ["foo"]), 213 | TestCase(longString, [longString]), 214 | TestCase(longString ~ "\n", [longString]), 215 | TestCase(longString ~ "\na" ~ longString, [longString, "a" ~ longString]), 216 | TestCase(longString2, [longString2]), 217 | TestCase(longString2 ~ "\n", [longString2]), 218 | TestCase(longString2 ~ "\na" ~ longString2, [longString2, "a" ~ longString2]), 219 | ]; 220 | foreach (testCase; testCases) 221 | { 222 | //import mar.file; stdout.write("TestCase '''\n", testCase.input, "\n'''\n"); 223 | foreach (readSize; 1 .. testCase.input.length + 1) 224 | { 225 | //import mar.file; stdout.write(" readSize ", readSize, "\n"); 226 | auto reader = LineReader!TestHooks(testCase.input, readSize); 227 | foreach (line; testCase.lines) 228 | { 229 | auto result = reader.readln(); 230 | assert(!result.failed); 231 | assert(result.val == line); 232 | } 233 | { 234 | auto result = reader.readln(); 235 | assert(!result.failed); 236 | assert(result.val.length == 0); 237 | } 238 | } 239 | } 240 | } 241 | 242 | // TODO: support the ability to get the file size beforehand 243 | ReadResult readAllMalloc(mar.file.FileD file, size_t initialSize = 4096) 244 | { 245 | import mar.mem : malloc, free, reallocOrSaveArray; 246 | import mar.file : read; 247 | 248 | char[] buffer; 249 | { 250 | auto ptr = malloc(initialSize); 251 | if (ptr is null) 252 | return ReadResult.outOfMemory; 253 | buffer = (cast(char*)ptr)[0 .. initialSize]; 254 | } 255 | size_t totalRead = 0; 256 | 257 | for (;;) 258 | { 259 | { 260 | //import mar.stdio; stdout.writeln("[DEBUG] read..."); 261 | auto result = read(file, buffer[totalRead .. $]); 262 | //import mar.stdio; stdout.writeln("[DEBUG] read returned ", result.numval); 263 | if (result.numval <= 0) 264 | { 265 | if (result.failed) 266 | return ReadResult.readError(result.numval); 267 | return ReadResult.success(buffer[0 .. totalRead]); 268 | } 269 | totalRead += result.val; 270 | } 271 | if (totalRead >= buffer.length) 272 | { 273 | auto newBuffer = reallocOrSaveArray(buffer.ptr, buffer.length * 2, totalRead); 274 | if (newBuffer is null) 275 | { 276 | free(buffer.ptr); 277 | return ReadResult.outOfMemory; 278 | } 279 | buffer = newBuffer; 280 | } 281 | } 282 | 283 | } -------------------------------------------------------------------------------- /src/mar/intfromchars.d: -------------------------------------------------------------------------------- 1 | /** 2 | A small module that creates integers from strings. 3 | */ 4 | module mar.intfromchars; 5 | 6 | /** 7 | Creates a single integer from a string of ascii characters. 8 | 9 | Example 10 | --- 11 | assert(0x61626364 == IntFromChars!"abcd"); 12 | --- 13 | */ 14 | template IntFromChars(string chars) 15 | { 16 | enum IntFromChars = mixin((){ 17 | string hexTable = "0123456789abcdef"; 18 | auto result = new char[2 + (2 * chars.length)]; 19 | result[0 .. 2] = "0x"; 20 | foreach (i; 0 .. chars.length) 21 | { 22 | result[2 + (2 * i) + 0] = hexTable[(chars[i] >> 4) & 0xF]; 23 | result[2 + (2 * i) + 1] = hexTable[(chars[i] >> 0) & 0xF]; 24 | } 25 | return result; 26 | }()); 27 | } 28 | unittest 29 | { 30 | assert(0x61 == IntFromChars!"a"); 31 | assert(0x5a == IntFromChars!"Z"); 32 | assert(0x61626364 == IntFromChars!"abcd"); 33 | } -------------------------------------------------------------------------------- /src/mar/linux/cthunk/.gitignore: -------------------------------------------------------------------------------- 1 | gencthunk 2 | -------------------------------------------------------------------------------- /src/mar/linux/cthunk/gen.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | gcc -o gencthunk gencthunk.c 3 | ./gencthunk > package.d 4 | cat package.d 5 | -------------------------------------------------------------------------------- /src/mar/linux/cthunk/gencthunk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define memberSize(type, member) sizeof(((type*)0)->member) 7 | 8 | int errorCount = 0; 9 | 10 | const char *getUnsignedType(unsigned size) 11 | { 12 | switch (size) { 13 | case 1: return "ubyte"; 14 | case 2: return "ushort"; 15 | case 4: return "uint"; 16 | case 8: return "ulong"; 17 | } 18 | fprintf(stderr, "Error: unsupported unsigned type size %d\n", size); 19 | errorCount++; 20 | return "???"; 21 | } 22 | const char *getSignedType(unsigned size) 23 | { 24 | switch (size) { 25 | case 1: return "byte"; 26 | case 2: return "short"; 27 | case 4: return "int"; 28 | case 8: return "long"; 29 | } 30 | fprintf(stderr, "Error: unsupported signed type size %d\n", size); 31 | errorCount++; 32 | return "???"; 33 | } 34 | int main() 35 | { 36 | printf("/**\n"); 37 | printf("File generated from C source to define types in D\n"); 38 | printf("*/\n"); 39 | printf("module mar.linux.cthunk;\n"); 40 | printf("\n"); 41 | printf("alias mode_t = %s;\n", getUnsignedType(sizeof(mode_t))); 42 | printf("alias ino_t = %s;\n", getUnsignedType(sizeof(ino_t))); 43 | printf("alias dev_t = %s;\n", getUnsignedType(sizeof(dev_t))); 44 | printf("alias nlink_t = %s;\n", getUnsignedType(sizeof(nlink_t))); 45 | printf("alias uid_t = %s;\n", getUnsignedType(sizeof(uid_t))); 46 | printf("alias gid_t = %s;\n", getUnsignedType(sizeof(gid_t))); 47 | printf("alias off_t = %s;\n", getUnsignedType(sizeof(off_t))); 48 | printf("alias loff_t = %s;\n", getUnsignedType(sizeof(loff_t))); 49 | printf("alias blksize_t = %s;\n", getUnsignedType(sizeof(blksize_t))); 50 | printf("alias blkcnt_t = %s;\n", getUnsignedType(sizeof(blkcnt_t))); 51 | printf("alias time_t = %s;\n", getSignedType(sizeof(time_t))); 52 | printf("\n"); 53 | printf("// important to make sure the size of the struct matches to prevent\n"); 54 | printf("// buffer overlows when padding addresses of stat buffers allocated\n"); 55 | printf("// on the stack\n"); 56 | printf("enum sizeofStructStat = %lu;\n", sizeof(struct stat)); 57 | printf("struct kernel\n"); 58 | printf("{\n"); 59 | printf(" alias unsigned_int = %s;\n", getUnsignedType(sizeof(unsigned int))); 60 | printf(" alias unsigned_long = %s;\n", getUnsignedType(sizeof(unsigned long))); 61 | printf("}\n"); 62 | return errorCount; 63 | } 64 | -------------------------------------------------------------------------------- /src/mar/linux/cthunk/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | File generated from C source to define types in D 3 | */ 4 | module mar.linux.cthunk; 5 | 6 | alias mode_t = uint; 7 | alias ino_t = ulong; 8 | alias dev_t = ulong; 9 | alias nlink_t = ulong; 10 | alias uid_t = uint; 11 | alias gid_t = uint; 12 | alias off_t = ulong; 13 | alias loff_t = ulong; 14 | alias blksize_t = ulong; 15 | alias blkcnt_t = ulong; 16 | alias time_t = long; 17 | 18 | // important to make sure the size of the struct matches to prevent 19 | // buffer overlows when padding addresses of stat buffers allocated 20 | // on the stack 21 | enum sizeofStructStat = 144; 22 | struct kernel 23 | { 24 | alias unsigned_int = uint; 25 | alias unsigned_long = ulong; 26 | } 27 | -------------------------------------------------------------------------------- /src/mar/linux/file/package.d: -------------------------------------------------------------------------------- 1 | module mar.linux.file; 2 | 3 | public import mar.linux.file.perm; 4 | 5 | import mar.passfail; 6 | import mar.typecons : ValueOrErrorCode; 7 | import mar.wrap; 8 | import mar.c; 9 | import mar.linux.cthunk; 10 | import mar.linux.syscall; 11 | 12 | alias open = sys_open; 13 | alias openat = sys_openat; 14 | alias close = sys_close; 15 | alias read = sys_read; 16 | alias write = sys_write; 17 | alias pipe = sys_pipe; 18 | alias lseek = sys_lseek; 19 | alias access = sys_access; 20 | alias dup2 = sys_dup2; 21 | alias fstatat = sys_newfstatat; 22 | alias stat = sys_stat; 23 | alias fstat = sys_fstat; 24 | alias lstat = sys_lstat; 25 | 26 | struct WriteResult 27 | { 28 | ptrdiff_t _value; 29 | 30 | bool failed() const { pragma(inline, true); return _value != 0; } 31 | bool passed() const { pragma(inline, true); return _value == 0; } 32 | 33 | size_t onFailWritten() const in { assert(_value != 0, "code bug"); } do 34 | { pragma(inline, true); return (_value > 0) ? _value : 0; } 35 | 36 | short errorCode() const in { assert(_value != 0, "code bug"); } do 37 | // TODO: replace -5 with -EIO 38 | { pragma(inline, true); return (_value > 0) ? -5 : cast(short)_value; } 39 | } 40 | extern (C) WriteResult tryWrite(FileD handle, const(void)* ptr, size_t n) 41 | { 42 | size_t initialSize = n; 43 | auto result = write(handle, ptr, n); 44 | return (result.val == n) ? WriteResult(0) : WriteResult(result.numval); 45 | } 46 | 47 | struct FileD 48 | { 49 | private int _value = -1; 50 | this(typeof(_value) value) pure nothrow @nogc 51 | { 52 | this._value = value; 53 | } 54 | bool isValid() const { return _value >= 0; } 55 | auto numval() const { pragma(inline, true); return _value; } 56 | void setInvalid() { this._value = -1; } 57 | mixin WrapperFor!"_value"; 58 | mixin WrapOpCast; 59 | 60 | final void close() const 61 | { 62 | pragma(inline, true); 63 | .close(this); 64 | } 65 | 66 | auto print(P)(P printer) const 67 | { 68 | import mar.print : printDecimal; 69 | return printDecimal(printer, _value); 70 | } 71 | 72 | WriteResult tryWrite(const(void)* ptr, size_t length) { pragma(inline, true); return .tryWrite(this, ptr, length); } 73 | WriteResult tryWrite(const(void)[] array) { pragma(inline, true); return .tryWrite(this, array.ptr, array.length); } 74 | 75 | void write(T...)(T args) const 76 | { 77 | import mar.print : DefaultBufferedFilePrinterPolicy, BufferedFilePrinter, printArgs; 78 | alias Printer = BufferedFilePrinter!DefaultBufferedFilePrinterPolicy; 79 | 80 | char[DefaultBufferedFilePrinterPolicy.bufferLength] buffer; 81 | auto printer = Printer(this, buffer.ptr, 0); 82 | printArgs(&printer, args); 83 | printer.flush(); 84 | } 85 | 86 | void writeln(T...)(T args) const 87 | { 88 | pragma(inline, true); 89 | write(args, '\n'); 90 | } 91 | 92 | passfail tryFlush() const 93 | { 94 | pragma(inline, true); 95 | return passfail.pass; 96 | } 97 | 98 | auto read(T)(T[] buffer) const if (T.sizeof <= 1) 99 | { 100 | pragma(inline, true); 101 | return sys_read(this, buffer.ptr, buffer.length); 102 | } 103 | } 104 | 105 | auto write(T)(FileD fd, T[] buffer) if (T.sizeof == 1) 106 | { 107 | // Error: function `mar.linux.file.write!(immutable(char)).write` cannot inline function 108 | //pragma(inline, true); 109 | return sys_write(fd, cast(const(void)*)buffer.ptr, buffer.length); 110 | } 111 | 112 | auto read(T)(FileD fd, T[] buffer) if (T.sizeof == 1) 113 | { 114 | pragma(inline, true); 115 | return sys_read(fd, cast(void*)buffer.ptr, buffer.length); 116 | } 117 | 118 | // TODO: rename to file access? 119 | enum OpenAccess : ubyte 120 | { 121 | readOnly = 0b00, // O_RDONLY 122 | writeOnly = 0b01, // O_WRONLY 123 | readWrite = 0b10, // O_RDWR 124 | } 125 | enum OpenCreateFlags : uint 126 | { 127 | none = 0, 128 | creat = 0x00040, // O_CREAT 129 | excl = 0x00080, // O_EXCL 130 | noCtty = 0x00100, // O_NOCTTY 131 | trunc = 0x00200, // O_TRUNC 132 | dir = 0x10000, // O_DIRECTORY 133 | noFollow = 0x20000, // O_NOFOLLOW 134 | closeOnExec = 0x80000, // O_CLOEXEC, 135 | } 136 | enum OpenStatusFlags : uint 137 | { 138 | __, 139 | } 140 | struct OpenFlags 141 | { 142 | uint _value; 143 | mixin WrapperFor!"_value"; 144 | mixin WrapOpCast; 145 | 146 | this(OpenAccess access, OpenCreateFlags flags = OpenCreateFlags.none) 147 | { 148 | this._value = cast(uint)flags | cast(uint)access; 149 | } 150 | ref uint val() { return _value; } 151 | } 152 | 153 | auto open(T)(T pathname, OpenFlags flags) if (!is(pathname : cstring)) 154 | { 155 | pragma(inline, true); 156 | mixin tempCString!("pathnameCStr", "pathname"); 157 | return sys_open(pathnameCStr, flags); 158 | } 159 | 160 | enum AccessMode 161 | { 162 | exists = 0, 163 | exec = 0b001, 164 | write = 0b010, 165 | read = 0b100, 166 | } 167 | 168 | enum SeekFrom 169 | { 170 | start = 0, 171 | current = 1, 172 | end = 2, 173 | } 174 | 175 | struct stat_t { 176 | union 177 | { 178 | struct 179 | { 180 | dev_t st_dev; // ID of device containing file 181 | ino_t st_ino; // inode number 182 | nlink_t st_nlink; // number of hard links 183 | mode_t st_mode; // protection 184 | uid_t st_uid; // user ID of owner 185 | gid_t st_gid; // group ID of owner 186 | dev_t st_rdev; // device ID (if special file) 187 | off_t st_size; // total size, in bytes 188 | blksize_t st_blksize; // blocksize for file system I/O 189 | blkcnt_t st_blocks; // number of 512B blocks allocated 190 | time_t st_atime; // time of last access 191 | time_t st_mtime; // time of last modification 192 | time_t st_ctime; // time of last status change 193 | } 194 | ubyte[sizeofStructStat] _reserved; 195 | } 196 | }; 197 | 198 | enum AT_SYMLINK_NOFOLLOW = 0x100; // Do not follow symbolic links 199 | 200 | //ValueOrErrorCode!(size_t, SyscallResult) readlinkat(FileD dirFd, cstring pathname, char[] buffer) 201 | 202 | auto readlinkat(FileD dirFd, cstring pathname, char[] buffer) 203 | { 204 | pragma(inline, true); 205 | return sys_readlinkat(dirFd, pathname, buffer.ptr, buffer.length); 206 | /* 207 | auto result = syscall(Syscall.readlinkat, dirFd, pathname, buffer.ptr, buffer.length); 208 | return (result > 0) ? 209 | ValueOrErrorCode!(size_t, SyscallResult)(result): 210 | ValueOrErrorCode!(size_t, SyscallResult).error(result); 211 | */ 212 | } 213 | 214 | version (D_BetterC) { } else { 215 | 216 | class FileException : Exception 217 | { 218 | /* 219 | private bool nameIsArray; 220 | union 221 | { 222 | private const(char)[] nameArray; 223 | private cstring nameCString; 224 | } 225 | */ 226 | this(string msg, string file = __FILE__, 227 | size_t line = cast(size_t)__LINE__) 228 | { 229 | super(msg, file, line); 230 | } 231 | this(const(char)[] name, string msg, string file = __FILE__, 232 | size_t line = cast(size_t)__LINE__) 233 | { 234 | super(msg, file, line); 235 | //this.nameIsArray = true; 236 | //this.nameArray = name; 237 | } 238 | this(cstring name, string msg, string file = __FILE__, 239 | size_t line = cast(size_t)__LINE__) 240 | { 241 | import mar.string : strlen; 242 | super(msg, file, line); 243 | //auto namelen = strlen(name); 244 | //this.nameIsArray = true; 245 | //this.nameArray = name.ptr[0 .. namelen].idup; 246 | } 247 | } 248 | } 249 | 250 | bool fileExists(cstring filename) 251 | { 252 | // TODO: check if the error code is appropriate (file does not exist) 253 | return access(filename, AccessMode.exists).passed; 254 | } 255 | 256 | bool isDir(cstring path) 257 | { 258 | stat_t status = void; 259 | auto result = sys_stat(path, &status); 260 | return result.passed && mar.linux.file.perm.isDir(status.st_mode); 261 | } 262 | 263 | ValueOrErrorCode!(off_t, short) tryGetFileSize(cstring filename) 264 | { 265 | stat_t status = void; 266 | auto result = sys_stat(filename, &status); 267 | return result.passed ? typeof(return)(status.st_size) : 268 | typeof(return).error(cast(short)result.numval); 269 | 270 | } 271 | 272 | auto tryGetFileSize(T)(T filename) 273 | { 274 | pragma(inline, true); 275 | mixin tempCString!("filenameCStr", "filename"); 276 | return tryGetFileSize(filenameCStr.str); 277 | } 278 | 279 | ValueOrErrorCode!(mode_t, short) tryGetFileMode(cstring filename) 280 | { 281 | stat_t status = void; 282 | auto result = sys_stat(filename, &status); 283 | return result.passed ? typeof(return)(status.st_mode) : 284 | typeof(return).error(cast(short)result.numval); 285 | } 286 | 287 | 288 | // TODO: move this somewhere else 289 | void unreportableError(string Ex, T)(T msg, string file = __FILE__, size_t line = cast(size_t)__LINE__) 290 | { 291 | version (D_BetterC) 292 | { 293 | version (NoExit) 294 | static assert(0, "unreportableError cannot be called with BetterC and version=NoExit"); 295 | 296 | import mar.process : exit; 297 | import mar.stdio; 298 | // write error to stderr 299 | stderr.writeln("unreportable error: ", file, ": ", msg); 300 | exit(1); 301 | } 302 | else 303 | { 304 | mixin("throw new " ~ Ex ~ "(msg, file, line);"); 305 | } 306 | } 307 | 308 | mixin template GetFileSizeFuncs() 309 | { 310 | off_t getFileSize(cstring filename) 311 | { 312 | auto result = tryGetFileSize(filename); 313 | if (result.failed) 314 | unreportableError!"FileException"("stat function failed"); 315 | //if (result.failed) throw new FileException(filename, text( 316 | // "stat function failed (file='", filename, " e=", result.errorCode)); 317 | //if (result.failed) throw new FileException(filename, "stat failed"); 318 | return result.val; 319 | } 320 | auto getFileSize(T)(T filename) 321 | { 322 | pragma(inline, true); 323 | mixin tempCString!("filenameCStr", "filename"); 324 | return getFileSize(filenameCStr.str); 325 | } 326 | } 327 | 328 | // TODO: support NoExit 329 | version (NoExit) 330 | { 331 | version (D_BetterC) { } else 332 | { 333 | mixin GetFileSizeFuncs; 334 | } 335 | } 336 | else 337 | { 338 | mixin GetFileSizeFuncs; 339 | } 340 | 341 | struct PipeFds 342 | { 343 | FileD read; 344 | FileD write; 345 | } 346 | -------------------------------------------------------------------------------- /src/mar/linux/file/perm.d: -------------------------------------------------------------------------------- 1 | module mar.file.perm; 2 | 3 | import mar.linux.cthunk : mode_t; 4 | 5 | enum ModeFlags : mode_t 6 | { 7 | execOther = 0b000_000_000_000_000_001, 8 | writeOther = 0b000_000_000_000_000_010, 9 | readOther = 0b000_000_000_000_000_100, 10 | execGroup = 0b000_000_000_000_001_000, 11 | writeGroup = 0b000_000_000_000_010_000, 12 | readGroup = 0b000_000_000_000_100_000, 13 | execUser = 0b000_000_000_001_000_000, 14 | writeUser = 0b000_000_000_010_000_000, 15 | readUser = 0b000_000_000_100_000_000, 16 | isFifo = 0b000_001_000_000_000_000, 17 | isCharDev = 0b000_010_000_000_000_000, 18 | isDir = 0b000_100_000_000_000_000, 19 | isRegFile = 0b001_000_000_000_000_000, 20 | } 21 | 22 | enum ModeTypeMask = ( 23 | ModeFlags.isFifo | 24 | ModeFlags.isCharDev | 25 | ModeFlags.isDir | 26 | ModeFlags.isRegFile); 27 | enum ModeTypeLink = ModeFlags.isCharDev | ModeFlags.isRegFile; 28 | enum ModeTypeDir = ModeFlags.isDir; 29 | 30 | /* 31 | #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) 32 | #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 33 | #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 34 | #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) 35 | #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) 36 | #define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) 37 | #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) 38 | */ 39 | 40 | bool isLink(const mode_t mode) 41 | { 42 | pragma(inline, true); 43 | return (mode & ModeTypeMask) == ModeTypeLink; 44 | } 45 | bool isDir(const mode_t mode) 46 | { 47 | pragma(inline, true); 48 | return (mode & ModeTypeMask) == ModeTypeDir; 49 | } 50 | 51 | auto formatMode(mode_t mode) 52 | { 53 | static struct Formatter 54 | { 55 | mode_t mode; 56 | auto print(P)(P printer) const 57 | { 58 | auto buffer = printer.getTempBuffer!10; 59 | scope (exit) printer.commitBuffer(buffer.commitValue); 60 | 61 | if (mode.isLink) 62 | buffer.putc('l'); 63 | else if (mode.isDir) 64 | buffer.putc('d'); 65 | else 66 | buffer.putc('-'); 67 | buffer.putc((mode & ModeFlags.readUser) ? 'r' : '-'); 68 | buffer.putc((mode & ModeFlags.writeUser) ? 'w' : '-'); 69 | buffer.putc((mode & ModeFlags.execUser) ? 'x' : '-'); 70 | buffer.putc((mode & ModeFlags.readGroup) ? 'r' : '-'); 71 | buffer.putc((mode & ModeFlags.writeGroup) ? 'w' : '-'); 72 | buffer.putc((mode & ModeFlags.execGroup) ? 'x' : '-'); 73 | buffer.putc((mode & ModeFlags.readOther) ? 'r' : '-'); 74 | buffer.putc((mode & ModeFlags.writeOther) ? 'w' : '-'); 75 | buffer.putc((mode & ModeFlags.execOther) ? 'x' : '-'); 76 | return P.success; 77 | } 78 | } 79 | return Formatter(mode); 80 | } 81 | 82 | enum S_IXOTH = ModeFlags.execOther; 83 | enum S_IWOTH = ModeFlags.writeOther; 84 | enum S_IROTH = ModeFlags.readOther; 85 | enum S_IRWXO = ModeFlags.readOther | ModeFlags.writeOther | ModeFlags.execOther; 86 | 87 | enum S_IXGRP = ModeFlags.execGroup; 88 | enum S_IWGRP = ModeFlags.writeGroup; 89 | enum S_IRGRP = ModeFlags.readGroup; 90 | enum S_IRWXG = ModeFlags.readGroup | ModeFlags.writeGroup | ModeFlags.execGroup; 91 | 92 | enum S_IXUSR = ModeFlags.execUser; 93 | enum S_IWUSR = ModeFlags.writeUser; 94 | enum S_IRUSR = ModeFlags.readUser; 95 | enum S_IRWXU = ModeFlags.readUser | ModeFlags.writeUser | ModeFlags.execUser; 96 | -------------------------------------------------------------------------------- /src/mar/linux/filesys.d: -------------------------------------------------------------------------------- 1 | module mar.linux.filesys; 2 | 3 | import mar.typecons : ValueOrErrorCode; 4 | import mar.sentinel : SentinelPtr, assumeSentinel; 5 | import mar.c : cstring, tempCString; 6 | import mar.linux.file : FileD; 7 | import mar.linux.cthunk : kernel, mode_t; 8 | import mar.linux.syscall; 9 | 10 | alias mkdir = sys_mkdir; 11 | alias rmdir = sys_rmdir; 12 | alias link = sys_link; 13 | alias unlink = sys_unlink; 14 | alias symlink = sys_symlink; 15 | alias readlink = sys_readlink; 16 | alias getcwd = sys_getcwd; 17 | alias chdir = sys_chdir; 18 | alias chroot = sys_chroot; 19 | alias mount = sys_mount; 20 | alias umount2 = sys_umount2; 21 | alias getdents = sys_getdents; 22 | alias umask = sys_umask; 23 | 24 | auto chdir(T)(T path) if (!is(path : cstring)) 25 | { 26 | pragma(inline, true); 27 | mixin tempCString!("pathCStr", "path"); 28 | return sys_chdir(pathCStr.str); 29 | } 30 | 31 | auto readlink(cstring path, char[] buffer) 32 | { 33 | pragma(inline, true); 34 | return readlink(path, buffer.ptr, buffer.length); 35 | } 36 | 37 | /** 38 | Taken from http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html 39 | */ 40 | /+ 41 | TODO: port to D 42 | void getcwd2() 43 | { 44 | typedef std::pair file_id; 45 | 46 | bool success = false; 47 | int start_fd = open(".", O_RDONLY); //Keep track of start directory, so can jump back to it later 48 | if (start_fd != -1) 49 | { 50 | struct stat sb; 51 | if (!fstat(start_fd, &sb)) 52 | { 53 | file_id current_id(sb.st_dev, sb.st_ino); 54 | if (!stat("/", &sb)) //Get info for root directory, so we can determine when we hit it 55 | { 56 | std::vector path_components; 57 | file_id root_id(sb.st_dev, sb.st_ino); 58 | 59 | while (current_id != root_id) //If they're equal, we've obtained enough info to build the path 60 | { 61 | bool pushed = false; 62 | 63 | if (!chdir("..")) //Keep recursing towards root each iteration 64 | { 65 | DIR *dir = opendir("."); 66 | if (dir) 67 | { 68 | dirent *entry; 69 | while ((entry = readdir(dir))) //We loop through each entry trying to find where we came from 70 | { 71 | if ((strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..") && !lstat(entry->d_name, &sb))) 72 | { 73 | file_id child_id(sb.st_dev, sb.st_ino); 74 | if (child_id == current_id) //We found where we came from, add its name to the list 75 | { 76 | path_components.push_back(entry->d_name); 77 | pushed = true; 78 | break; 79 | } 80 | } 81 | } 82 | closedir(dir); 83 | 84 | if (pushed && !stat(".", &sb)) //If we have a reason to contiue, we update the current dir id 85 | { 86 | current_id = file_id(sb.st_dev, sb.st_ino); 87 | } 88 | }//Else, Uh oh, can't read information at this level 89 | } 90 | if (!pushed) { break; } //If we didn't obtain any info this pass, no reason to continue 91 | } 92 | 93 | if (current_id == root_id) //Unless they're equal, we failed above 94 | { 95 | //Built the path, will always end with a slash 96 | path = "/"; 97 | for (std::vector::reverse_iterator i = path_components.rbegin(); i != path_components.rend(); ++i) 98 | { 99 | path += *i+"/"; 100 | } 101 | success = true; 102 | } 103 | fchdir(start_fd); 104 | } 105 | } 106 | close(start_fd); 107 | } 108 | 109 | return(success); 110 | } 111 | +/ 112 | 113 | 114 | // taken from the latest kernel...however, this 115 | // structure changes over time 116 | struct linux_dirent 117 | { 118 | align(1): 119 | ulong d_ino; /// inode 120 | ulong d_off; /// offset to next 121 | ushort d_reclen; /// length of this dirent 122 | private char[0] d_name; 123 | auto nameCString() inout 124 | { 125 | import mar.sentinel : assumeSentinel; 126 | return (cast(char*)(&d_name)).assumeSentinel; 127 | } 128 | } 129 | /* 130 | struct linux_dirent64 131 | { 132 | align(1): 133 | ulong d_ino; /// inode 134 | ulong d_off; /// offset to next 135 | ushort d_reclen; /// length of this dirent 136 | //ubyte d_type; 137 | private char[0] d_name; 138 | 139 | auto nameCString() inout 140 | { 141 | import mar.sentinel : assumeSentinel; 142 | return (cast(char*)(&d_name)).assumeSentinel; 143 | } 144 | } 145 | */ 146 | 147 | // TODO: make one that supports NoExit 148 | version (NoExit) { } else 149 | { 150 | struct LinuxDirentRange 151 | { 152 | size_t size; 153 | linux_dirent* next; 154 | bool empty() const { return size == 0; } 155 | auto front() { return next; } 156 | void popFront() 157 | { 158 | import mar.stdio : stderr; 159 | import mar.process : exit; 160 | if (next[0].d_reclen > size) 161 | { 162 | stderr.writeln("Error: invalid linux_dirent, size is ", size, " but d_reclen is ", 163 | next[0].d_reclen); 164 | exit(1); 165 | } 166 | size -= next[0].d_reclen; 167 | next = cast(linux_dirent*)((cast(ubyte*)next) + next[0].d_reclen); 168 | } 169 | } 170 | } 171 | 172 | 173 | // These are the fs-independent mount-flags: up to 32 flags are supported 174 | enum MS_RDONLY = 1 << 0; // Mount read-only 175 | enum MS_NOSUID = 1 << 1; // Ignore suid and sgid bits 176 | enum MS_NODEV = 1 << 2; // Disallow access to device special files 177 | enum MS_NOEXEC = 1 << 3; // Disallow program execution 178 | enum MS_SYNCHRONOUS = 1 << 4; // Writes are synced at once 179 | enum MS_REMOUNT = 1 << 5; // Alter flags of a mounted FS 180 | enum MS_MANDLOCK = 1 << 6; // Allow mandatory locks on an FS 181 | enum MS_DIRSYNC = 1 << 7; // Directory modifications are synchronous 182 | enum MS_NOATIME = 1 << 10; // Do not update access times. 183 | enum MS_NODIRATIME = 1 << 11; // Do not update directory access times 184 | enum MS_BIND = 1 << 12; 185 | enum MS_MOVE = 1 << 13; 186 | enum MS_REC = 1 << 14; 187 | enum MS_VERBOSE = 1 << 15; // War is peace. Verbosity is silence. 188 | // MS_VERBOSE is deprecated. 189 | enum MS_SILENT = 1 << 15; 190 | enum MS_POSIXACL = 1 << 16; // VFS does not apply the umask 191 | enum MS_UNBINDABLE = 1 << 17; // change to unbindable 192 | enum MS_PRIVATE = 1 << 18; // change to private 193 | enum MS_SLAVE = 1 << 19; // change to slave 194 | enum MS_SHARED = 1 << 20; // change to shared 195 | enum MS_RELATIME = 1 << 21; // Update atime relative to mtime/ctime. 196 | enum MS_KERNMOUNT = 1 << 22; // this is a kern_mount call 197 | enum MS_I_VERSION = 1 << 23; // Update inode I_version field 198 | enum MS_STRICTATIME = 1 << 24; // Always perform atime updates 199 | enum MS_LAZYTIME = 1 << 25; // Update the on-disk [acm]times lazily 200 | 201 | 202 | enum AT_FDCWD = -100; // Special value used to indicate 203 | // openat should use the current 204 | // working directory. 205 | enum AT_SYMLINK_NOFOLLOW = 0x0100; // Do not follow symbolic links. 206 | enum AT_REMOVEDIR = 0x0200; // Remove directory instead of 207 | // unlinking file. 208 | enum AT_SYMLINK_FOLLOW = 0x0400; // Follow symbolic links. 209 | enum AT_NO_AUTOMOUNT = 0x0800; // Suppress terminal automount traversal 210 | enum AT_EMPTY_PATH = 0x1000; // Allow empty relative pathname 211 | -------------------------------------------------------------------------------- /src/mar/linux/ioctl.d: -------------------------------------------------------------------------------- 1 | module mar.linux.ioctl; 2 | 3 | import mar.linux.file : FileD; 4 | 5 | import mar.linux.syscall; 6 | 7 | /** 8 | ioctl command encoding: 32 bits total, command in lower 16 bits, 9 | size of the parameter structure in the lower 14 bits of the 10 | upper 16 bits. 11 | Encoding the size of the parameter structure in the ioctl request 12 | is useful for catching programs compiled with old versions 13 | and to avoid overwriting user space outside the user buffer area. 14 | The highest 2 bits are reserved for indicating the ``access mode''. 15 | NOTE: This limits the max parameter size to 16kB -1 ! 16 | 17 | The following is for compatibility across the various Linux 18 | platforms. The generic ioctl numbering scheme doesn't really enforce 19 | a type field. De facto, however, the top 8 bits of the lower 16 20 | bits are indeed used as a type field, so we might just as well make 21 | this explicit here. Please be sure to use the decoding macros 22 | below from now on. 23 | */ 24 | 25 | enum _IOC_NRBITS = 8; 26 | enum _IOC_TYPEBITS = 8; 27 | enum _IOC_SIZEBITS = 14; 28 | enum _IOC_DIRBITS = 2; 29 | 30 | enum _IOC_NRMASK = (1 << _IOC_NRBITS ) - 1; 31 | enum _IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1; 32 | enum _IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1; 33 | enum _IOC_DIRMASK = (1 << _IOC_DIRBITS ) - 1; 34 | 35 | enum _IOC_NRSHIFT = 0; 36 | enum _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS; 37 | enum _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS; 38 | enum _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS; 39 | 40 | enum _IOC_NONE = 0; 41 | enum _IOC_WRITE = 1; 42 | enum _IOC_READ = 2; 43 | 44 | uint _IOC(uint dir, uint type, uint nr, uint size) 45 | { 46 | return (dir << _IOC_DIRSHIFT) | 47 | (type << _IOC_TYPESHIFT) | 48 | (type << _IOC_TYPESHIFT) | 49 | (nr << _IOC_NRSHIFT) | 50 | (size << _IOC_SIZESHIFT) ; 51 | } 52 | 53 | 54 | //#ifndef __KERNEL__ 55 | //#define _IOC_TYPECHECK(t) (sizeof(t)) 56 | //#endif 57 | 58 | // Used to create numbers. 59 | // 60 | // NOTE: _IOW means userland is writing and kernel is reading. _IOR 61 | // means userland is reading and kernel is writing. 62 | uint _IO (uint type, uint nr) { return _IOC(_IOC_NONE , type, nr, 0); } 63 | uint _IOR (uint type, uint nr, uint size) { return _IOC(_IOC_READ , type, nr, size/*_IOC_TYPECHECK(size)*/); } 64 | uint _IOW (uint type, uint nr, uint size) { return _IOC(_IOC_WRITE , type, nr, size/*_IOC_TYPECHECK(size)*/); } 65 | uint _IOWR (uint type, uint nr, uint size) { return _IOC(_IOC_READ|_IOC_WRITE, type, nr, size/*_IOC_TYPECHECK(size)*/); } 66 | uint _IOR_BAD (uint type, uint nr, uint size) { return _IOC(_IOC_READ , type, nr, size); } 67 | uint _IOW_BAD (uint type, uint nr, uint size) { return _IOC(_IOC_WRITE , type, nr, size); } 68 | uint _IOWR_BAD(uint type, uint nr, uint size) { return _IOC(_IOC_READ|_IOC_WRITE, type, nr, size); } 69 | 70 | // used to decode ioctl numbers.. 71 | uint _IOC_DIR (uint nr) { return ((nr >> _IOC_DIRSHIFT ) & _IOC_DIRMASK); } 72 | uint _IOC_TYPE(uint nr) { return ((nr >> _IOC_TYPESHIFT) & _IOC_TYPEMASK); } 73 | uint _IOC_NR (uint nr) { return ((nr >> _IOC_NRSHIFT ) & _IOC_NRMASK); } 74 | uint _IOC_SIZE(uint nr) { return ((nr >> _IOC_SIZESHIFT) & _IOC_SIZEMASK); } 75 | 76 | /* 77 | // ...and for the drivers/sound files... 78 | uint IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) 79 | uint IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) 80 | uint IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) 81 | uint IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) 82 | uint IOCSIZE_SHIFT (_IOC_SIZESHIFT) 83 | */ 84 | 85 | alias ioctl = sys_ioctl; 86 | 87 | extern (C) auto ioctl(T)(FileD fd, size_t request, T arg) if (!is(arg : void*)) 88 | { 89 | pragma(inline, true); 90 | return sys_ioctl(fd, request, cast(void*)arg); 91 | } 92 | -------------------------------------------------------------------------------- /src/mar/linux/mem.d: -------------------------------------------------------------------------------- 1 | module mar.linux.mem; 2 | 3 | import mar.linux.syscall; 4 | 5 | SyscallValueResult!(void*) sbrk(ptrdiff_t increment) 6 | { 7 | pragma(inline, true); 8 | assert(0, "not implemented"); 9 | //import mar.linux.syscall; 10 | //return SyscallNegativeErrorOrValue!(void*)(syscall(Syscall.brk, increment)); 11 | } 12 | -------------------------------------------------------------------------------- /src/mar/linux/mmap.d: -------------------------------------------------------------------------------- 1 | module mar.linux.mmap; 2 | 3 | // TODO: move this to mar 4 | 5 | import mar.flag; 6 | import mar.typecons : ValueOrErrorCode; 7 | import mar.linux.cthunk : off_t; 8 | import mar.linux.file : FileD; 9 | import mar.linux.syscall; 10 | 11 | alias mmap = sys_mmap; 12 | alias munmap = sys_munmap; 13 | alias mremap = sys_mremap; 14 | 15 | enum PROT_NONE = 0b0000; 16 | enum PROT_READ = 0b0001; 17 | enum PROT_WRITE = 0b0010; 18 | enum PROT_EXEC = 0b0100; 19 | 20 | enum MAP_SHARED = 0b00_0001; 21 | enum MAP_PRIVATE = 0b00_0010; 22 | enum MAP_FIXED = 0b01_0000; 23 | enum MAP_ANONYMOUS = 0b10_0000; 24 | 25 | enum MREMAP_MAYMOVE = 1; 26 | enum MREMAP_FIXED = 2; 27 | 28 | enum MS_ASYNC = 1; 29 | enum MS_INVALIDATE = 2; 30 | enum MS_SYNC = 4; 31 | 32 | extern(C) int msync(void* addr, size_t length, int flags); 33 | 34 | struct MemoryMap 35 | { 36 | private SyscallValueResult!(ubyte*) _result; 37 | private size_t length; 38 | auto numval() const { return _result.numval; } 39 | auto failed() const { return _result.failed; } 40 | auto passed() const { return _result.passed; } 41 | 42 | ubyte* ptr() const { return _result.val; } 43 | void* voidptr() const { return _result.val; } 44 | T[] array(T)() const if (T.sizeof == 1) 45 | { 46 | return (cast(T*)_result.val)[0 .. length]; 47 | } 48 | ~this() { unmap(); } 49 | void unmap() 50 | { 51 | if (_result.passed && _result.val != null) 52 | { 53 | munmap(_result.val, length); 54 | _result.set(null); 55 | } 56 | } 57 | } 58 | MemoryMap createMemoryMap(void* addrHint, size_t length, 59 | Flag!"writeable" writeable, FileD fd, off_t fdOffset) 60 | { 61 | return MemoryMap(mmap(addrHint, length, PROT_READ | 62 | (writeable ? PROT_WRITE : 0), MAP_SHARED, fd, fdOffset), length); 63 | } 64 | -------------------------------------------------------------------------------- /src/mar/linux/process.d: -------------------------------------------------------------------------------- 1 | module mar.linux.process; 2 | 3 | import mar.expect; 4 | import mar.sentinel : SentinelPtr; 5 | import mar.c : cstring; 6 | import mar.linux.syscall; 7 | 8 | version (NoExit) { } else 9 | { 10 | alias exit = sys_exit; 11 | } 12 | alias getpid = sys_getpid; 13 | alias fork = sys_fork; 14 | alias vfork = sys_vfork; 15 | alias waitid = sys_waitid; 16 | alias setsid = sys_setsid; 17 | alias execve = sys_execve; 18 | alias clone = sys_clone; 19 | alias unshare = sys_unshare; 20 | 21 | alias pid_t = ptrdiff_t; 22 | alias uid_t = ptrdiff_t; 23 | 24 | alias __kernel_long_t = int; 25 | alias __kernel_time_t = __kernel_long_t; 26 | alias __kernel_suseconds_t = int; 27 | struct timeval 28 | { 29 | __kernel_time_t tv_sec; 30 | __kernel_suseconds_t tv_usec; 31 | } 32 | struct rusage 33 | { 34 | timeval ru_utime; 35 | timeval ru_stime; 36 | __kernel_long_t ru_maxrss; 37 | __kernel_long_t ru_ixrss; 38 | __kernel_long_t ru_idrss; 39 | __kernel_long_t ru_isrss; 40 | __kernel_long_t ru_minflt; 41 | __kernel_long_t ru_majflt; 42 | __kernel_long_t ru_nswap; 43 | __kernel_long_t ru_inblock; 44 | __kernel_long_t ru_outblock; 45 | __kernel_long_t ru_msgsnd; 46 | __kernel_long_t ru_msgrcv; 47 | __kernel_long_t ru_nsignals; 48 | __kernel_long_t ru_nvcsw; 49 | __kernel_long_t ru_nivcsw; 50 | } 51 | 52 | enum idtype_t 53 | { 54 | all = 0, 55 | pid = 1, 56 | pgid = 2, 57 | } 58 | enum WNOHANG = 0x00000001; 59 | enum WUNTRACED = 0x00000002; 60 | enum WSTOPPED = WUNTRACED; 61 | enum WEXITED = 0x00000004; 62 | enum WCONTINUED = 0x00000008; 63 | enum WNOWAIT = 0x01000000; 64 | 65 | enum __WNOTHREAD = 0x20000000; 66 | enum __WALL = 0x40000000; 67 | enum __WCLONE = 0x80000000; 68 | 69 | enum CSIGNAL = 0x000000ff; // signal mask to be sent at exit 70 | enum CLONE_VM = 0x00000100; // set if VM shared between processes 71 | enum CLONE_FS = 0x00000200; // set if fs info shared between processes 72 | enum CLONE_FILES = 0x00000400; // set if open files shared between processes 73 | enum CLONE_SIGHAND = 0x00000800; // set if signal handlers and blocked signals shared 74 | enum CLONE_PTRACE = 0x00002000; // set if we want to let tracing continue on the child too 75 | enum CLONE_VFORK = 0x00004000; // set if the parent wants the child to wake it up on mm_release 76 | enum CLONE_PARENT = 0x00008000; // set if we want to have the same parent as the cloner 77 | enum CLONE_THREAD = 0x00010000; // Same thread group? 78 | enum CLONE_NEWNS = 0x00020000; // New mount namespace group 79 | enum CLONE_SYSVSEM = 0x00040000; // share system V SEM_UNDO semantics 80 | enum CLONE_SETTLS = 0x00080000; // create a new TLS for the child 81 | enum CLONE_PARENT_SETTID = 0x00100000; // set the TID in the parent 82 | enum CLONE_CHILD_CLEARTID = 0x00200000; // clear the TID in the child 83 | enum CLONE_DETACHED = 0x00400000; // Unused, ignored 84 | enum CLONE_UNTRACED = 0x00800000; // set if the tracing process can't force CLONE_PTRACE on this clone 85 | enum CLONE_CHILD_SETTID = 0x01000000; // set the TID in the child 86 | enum CLONE_NEWCGROUP = 0x02000000; // New cgroup namespace 87 | enum CLONE_NEWUTS = 0x04000000; // New utsname namespace 88 | enum CLONE_NEWIPC = 0x08000000; // New ipc namespace 89 | enum CLONE_NEWUSER = 0x10000000; // New user namespace 90 | enum CLONE_NEWPID = 0x20000000; // New pid namespace 91 | enum CLONE_NEWNET = 0x40000000; // New network namespace 92 | enum CLONE_IO = 0x80000000; // Clone io context 93 | 94 | // 95 | // Scheduling policies 96 | // 97 | enum SCHED_NORMAL = 0; 98 | enum SCHED_FIFO = 1; 99 | enum SCHED_RR = 2; 100 | enum SCHED_BATCH = 3; 101 | // SCHED_ISO: reserved but not implemented yet 102 | //enum SCHED_IDLE = 5; 103 | //enum SCHED_DEADLINE = 6; 104 | 105 | // Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork 106 | enum SCHED_RESET_ON_FORK = 0x40000000; 107 | 108 | // 109 | // For the sched_{set,get}attr() calls 110 | // 111 | 112 | enum SCHED_FLAG_RESET_ON_FORK = 0x01; 113 | enum SCHED_FLAG_RECLAIM = 0x02; 114 | enum SCHED_FLAG_DL_OVERRUN = 0x04; 115 | 116 | enum SCHED_FLAG_ALL = (SCHED_FLAG_RESET_ON_FORK | 117 | SCHED_FLAG_RECLAIM | 118 | SCHED_FLAG_DL_OVERRUN); 119 | 120 | mixin ExpectMixin!("WaitResult", int, 121 | ErrorCase!("waitFailed", "wait failed, returned %", ptrdiff_t)); 122 | WaitResult wait(pid_t pid) 123 | { 124 | import mar.linux.signals : siginfo_t; 125 | siginfo_t info; 126 | auto result = waitid(idtype_t.pid, pid, &info, WEXITED, null); 127 | if (result.failed) 128 | return WaitResult.waitFailed(result.numval); 129 | return WaitResult.success(info.si_status); 130 | } 131 | 132 | /+ 133 | Expected!pid_t run(SentinelPtr!cstring argv, SentinelPtr!cstring envp) 134 | { 135 | // TODO: maybe I'm supposed to use posix_spawn instead of fork/exec? 136 | //auto pid = vfork(); 137 | auto pidResult = fork(); 138 | if (pidResult.failed) 139 | return unexpected("fork failed, returned ", pidResult.numval); 140 | 141 | if (pidResult.val == 0) 142 | { 143 | auto result = execve(argv[0], argv, envp); 144 | //logError("execve returned ", result.numval); 145 | exit(1); 146 | } 147 | return typeof(return)(pidResult.val); 148 | } 149 | +/ -------------------------------------------------------------------------------- /src/mar/linux/signals.d: -------------------------------------------------------------------------------- 1 | module mar.linux.signals; 2 | 3 | import mar.linux.syscall; 4 | import mar.linux.process : pid_t, uid_t; 5 | 6 | enum SIGHUP = 1; 7 | enum SIGINT = 2; 8 | enum SIGQUIT = 3; 9 | enum SIGILL = 4; 10 | enum SIGTRAP = 5; 11 | enum SIGABRT = 6; 12 | enum SIGIOT = 6; 13 | enum SIGBUS = 7; 14 | enum SIGFPE = 8; 15 | enum SIGKILL = 9; 16 | enum SIGUSR1 = 10; 17 | enum SIGSEGV = 11; 18 | enum SIGUSR2 = 12; 19 | enum SIGPIPE = 13; 20 | enum SIGALRM = 14; 21 | enum SIGTERM = 15; 22 | enum SIGSTKFLT = 16; 23 | enum SIGCHLD = 17; 24 | enum SIGCONT = 18; 25 | enum SIGSTOP = 19; 26 | enum SIGTSTP = 20; 27 | enum SIGTTIN = 21; 28 | enum SIGTTOU = 22; 29 | enum SIGURG = 23; 30 | enum SIGXCPU = 24; 31 | enum SIGXFSZ = 25; 32 | enum SIGVTALRM = 26; 33 | enum SIGPROF = 27; 34 | enum SIGWINCH = 28; 35 | enum SIGIO = 29; 36 | enum SIGPOLL = SIGIO; 37 | enum SIGPWR = 30; 38 | enum SIGSYS = 31; 39 | enum SIGUNUSED = 31; 40 | 41 | // These should not be considered constants from userland. 42 | enum SIGRTMIN = 32; 43 | enum SIGRTMAX = _NSIG; 44 | 45 | /* 46 | SA_FLAGS values: 47 | 48 | SA_ONSTACK indicates that a registered stack_t will be used. 49 | SA_RESTART flag to get restarting signals (which were the default long ago) 50 | SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. 51 | SA_RESETHAND clears the handler when the signal is delivered. 52 | SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. 53 | SA_NODEFER prevents the current signal from being masked in the handler. 54 | 55 | SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single 56 | Unix names RESETHAND and NODEFER respectively. 57 | */ 58 | enum SA_NOCLDSTOP = 0x00000001; 59 | enum SA_NOCLDWAIT = 0x00000002; 60 | enum SA_SIGINFO = 0x00000004; 61 | enum SA_ONSTACK = 0x08000000; 62 | enum SA_RESTART = 0x10000000; 63 | enum SA_NODEFER = 0x40000000; 64 | enum SA_RESETHAND = 0x80000000; 65 | 66 | enum SA_NOMASK = SA_NODEFER; 67 | enum SA_ONESHOT = SA_RESETHAND; 68 | 69 | enum _NSIG = 64; 70 | struct sigset_t 71 | { 72 | size_t[_NSIG / (8*size_t.sizeof)] sig; 73 | } 74 | 75 | union sigval 76 | { 77 | int sival_int; 78 | void* sival_ptr; 79 | } 80 | struct siginfo_t 81 | { 82 | size_t what_is_this1; // maybe signo? 83 | size_t what_is_this2; // maybe si_code? 84 | uint what_is_this3; // maybe si_errno? 85 | uint what_is_this4; 86 | int si_status; 87 | /+ 88 | I don't know the exact sizes of these fields or their exact position yet. 89 | sigval si_value; 90 | int si_errno; 91 | int si_status; 92 | pid_t si_pid; 93 | uid_t si_uid; 94 | void* si_addr; 95 | int si_band; 96 | +/ 97 | } 98 | 99 | struct sigaction_t 100 | { 101 | union 102 | { 103 | extern (C) void function(int) sa_handler; 104 | extern (C) void function(int, siginfo_t*, void*) sa_sigaction; 105 | } 106 | ulong sa_flags; 107 | extern (C) void function() sa_restorer; 108 | sigset_t sa_mask; 109 | } 110 | /+ 111 | struct stack_t 112 | { 113 | void __user *ss_sp; 114 | int ss_flags; 115 | size_t ss_size; 116 | } 117 | 118 | enum SS_ONSTACK = 1; 119 | enum SS_DISABLE = 2; 120 | 121 | // bit-flags 122 | enum SS_AUTODISARM = (1U << 31); // disable sas during sighandling 123 | // mask for all SS_xxx flags 124 | enum SS_FLAG_BITS = SS_AUTODISARM; 125 | +/ 126 | 127 | 128 | enum SIG_BLOCK = 0; 129 | enum SIG_UNBLOCK = 1; 130 | enum SIG_SETMASK = 2; 131 | 132 | enum SIG_DFL = cast(void function(int))0; // default signal handling 133 | enum SIG_IGN = cast(void function(int))1; // ignore signal 134 | enum SIG_ERR = cast(void function(int))2; // error return from signal 135 | 136 | private enum SA_RESTORER = 0x04000000; 137 | 138 | auto sigaction(int signum, const(sigaction_t)* act, sigaction_t* oldact) 139 | { 140 | sigaction_t kact; 141 | if (act) 142 | { 143 | kact.sa_handler = act.sa_handler; 144 | kact.sa_flags = act.sa_flags | SA_RESTORER; 145 | kact.sa_restorer = &sigreturn; 146 | kact.sa_mask = act.sa_mask; 147 | } 148 | return sys_rt_sigaction(signum, act ? &kact : null, oldact, sigset_t.sizeof); 149 | } 150 | 151 | void sigfillset(sigset_t* set) 152 | { 153 | set.sig[] = 0; 154 | } 155 | 156 | extern (C) void sigreturn() 157 | { 158 | // version (x86_64) 159 | asm 160 | { 161 | naked; 162 | mov EAX, 15; 163 | syscall; 164 | } 165 | /* 166 | version (x86) 167 | { 168 | naked 169 | mov EAX, 119; 170 | syscall; 171 | } 172 | */ 173 | } 174 | -------------------------------------------------------------------------------- /src/mar/linux/stdio.d: -------------------------------------------------------------------------------- 1 | module mar.linux.io; 2 | 3 | import mar.linux.file : FileD; 4 | 5 | const(FileD) stdin() pure nothrow @nogc { pragma(inline, true); return FileD(0); } 6 | const(FileD) stdout() pure nothrow @nogc { pragma(inline, true); return FileD(1); } 7 | const(FileD) stderr() pure nothrow @nogc { pragma(inline, true); return FileD(2); } 8 | -------------------------------------------------------------------------------- /src/mar/linux/vt.d: -------------------------------------------------------------------------------- 1 | /** 2 | Linux virtual terminal module. 3 | */ 4 | module mar.linux.vt; 5 | 6 | enum VT_OPENQRY = 0x5600; 7 | enum VT_GETSTATE = 0x5603; 8 | enum VT_ACTIVATE = 0x5606; 9 | enum VT_WAITACTIVE = 0x5607; 10 | -------------------------------------------------------------------------------- /src/mar/math.d: -------------------------------------------------------------------------------- 1 | module mar.math; 2 | 3 | version (NoStdc) 4 | { 5 | // sin function not implemented yet 6 | } 7 | else 8 | { 9 | extern (C) double sin(double x) @safe pure nothrow @nogc; 10 | private extern (C) float sinf(float x) @safe pure nothrow @nogc; 11 | float sin(float x) @safe pure nothrow @nogc { pragma(inline, true); return sinf(x); } 12 | } 13 | 14 | auto squared(T)(T value) { pragma(inline, true); return value * value; } 15 | 16 | auto abs(T)(T value) 17 | { 18 | pragma(inline, true); 19 | 20 | static if (__traits(isUnsigned, T)) 21 | return value; 22 | else 23 | return (value < 0) ? -value : value; 24 | } 25 | -------------------------------------------------------------------------------- /src/mar/mem/mmap.d: -------------------------------------------------------------------------------- 1 | /** 2 | An extremely dumb and expensive version of malloc. 3 | 4 | Currently only works on linux but support could be added for windows. 5 | */ 6 | module mar.mem_mmap; 7 | 8 | import mar.file : FileD; 9 | version (linux) 10 | { 11 | import mar.mmap : mmap, munmap, mremap, PROT_READ, PROT_WRITE, 12 | MAP_PRIVATE, MAP_ANONYMOUS, MREMAP_MAYMOVE; 13 | 14 | void* malloc(size_t size) 15 | { 16 | size += size_t.sizeof; 17 | auto map = mmap(null, size, PROT_READ | PROT_WRITE, 18 | MAP_PRIVATE | MAP_ANONYMOUS, FileD(-1), 0); 19 | if (map.failed) 20 | return null; 21 | (cast(size_t*)map.val)[0] = size; 22 | version (TraceMallocFree) 23 | { 24 | import mar.file : stdout; 25 | stdout.write("malloc(", size, ") > ", (map.val + size_t.sizeof), "\n"); 26 | } 27 | return map.val + size_t.sizeof; 28 | } 29 | void free(void* mem) 30 | { 31 | version (TraceMallocFree) 32 | { 33 | import mar.file : stdout; 34 | stdout.write("free(0x", mem, ")\n"); 35 | } 36 | if (mem) 37 | { 38 | mem = (cast(ubyte*)mem) - size_t.sizeof; 39 | auto size = (cast(size_t*)mem)[0]; 40 | auto result = munmap(cast(ubyte*)mem, size); 41 | assert(result.passed, "munmap failed"); 42 | } 43 | } 44 | 45 | // Returns: true if it resized, false otherwise 46 | bool tryRealloc(void* mem, size_t size) 47 | { 48 | version (TraceMallocFree) 49 | { 50 | import mar.file : stdout; 51 | stdout.write("realloc(", mem, ", ", size, ") > ", (map.val + size_t.sizeof), "\n"); 52 | } 53 | if (mem is null) 54 | return false; 55 | size += size_t.sizeof; 56 | auto base = mem - size_t.sizeof; 57 | return mremap(base, (cast(size_t*)base)[0], size, 0).failed ? false : true; 58 | } 59 | unittest 60 | { 61 | import mar.array; 62 | { 63 | auto result = malloc(100); 64 | assert(result); 65 | acopy(result, "1234567890"); 66 | assert((cast(char*)result)[0 .. 10] == "1234567890"); 67 | free(result); 68 | } 69 | } 70 | } 71 | else static assert(0, "mem.mmap module not implemented for this platform"); 72 | -------------------------------------------------------------------------------- /src/mar/mem/sbrk.d: -------------------------------------------------------------------------------- 1 | module mar.mem.sbrk; 2 | 3 | import mar.typecons : enforce; 4 | 5 | version (linux) 6 | { 7 | import mar.linux.mem : sbrk; 8 | } 9 | else static assert(0, "unsupported platform"); 10 | 11 | __gshared MemBlock* data_seg_ptr; 12 | __gshared void* data_seg_limit; 13 | 14 | static this() 15 | { 16 | version (linux) 17 | { 18 | import mar.linux.file; write(stdout, "[DEBUG] mar.mem static this!\n"); 19 | data_seg_ptr = cast(MemBlock*)sbrk(0).enforce("sbrk(0) failed"); 20 | // TODO: check the return value for success 21 | data_seg_limit = data_seg_ptr; 22 | } 23 | else static assert(0, "unsupported platform"); 24 | } 25 | 26 | private struct MemBlock 27 | { 28 | size_t size; 29 | bool available; 30 | } 31 | 32 | // TODO: support freeing null? 33 | void free(void* buffer) 34 | { 35 | MemBlock* mcb = cast(MemBlock*)(buffer - MemBlock.sizeof); 36 | mcb.available = true; 37 | } 38 | 39 | void* malloc(size_t size) 40 | { 41 | size += MemBlock.sizeof; 42 | MemBlock *ptr = data_seg_ptr; 43 | for (;; ptr += ptr.size) 44 | { 45 | if (ptr == data_seg_limit) 46 | { 47 | sbrk(size).enforce("sbrk failed"); 48 | data_seg_limit += size; 49 | ptr.size = size; 50 | break; 51 | } 52 | if (ptr.available && size <= ptr.size) 53 | { 54 | ptr.available = false; 55 | } 56 | } 57 | ptr.available = false; 58 | return ptr + MemBlock.sizeof; 59 | } 60 | -------------------------------------------------------------------------------- /src/mar/mem/windowsheap.d: -------------------------------------------------------------------------------- 1 | module mar.mem.windowsheap; 2 | 3 | import mar.enforce : enforce; 4 | import mar.windows.types : Handle, CriticalSection; 5 | import mar.windows.kernel32 : 6 | HeapCreateOptions, HeapAllocOptions, HeapFreeOptions, 7 | GetLastError, 8 | HeapCreate, HeapAlloc, HeapFree; 9 | 10 | // TODO: 11 | // need a way to initialize this!! 12 | private __gshared CriticalSection heapCS; 13 | 14 | private __gshared Handle defaultHeap = Handle.nullValue; 15 | private Handle tryGetDefaultHeap() 16 | { 17 | // double-checked locking 18 | if (defaultHeap.isNull) 19 | { 20 | //synchronized 21 | { 22 | if (defaultHeap.isNull) 23 | { 24 | // HeapCreateOptions.noSerialize because we are already synchronized 25 | auto heap = HeapCreate(HeapCreateOptions.noSerialize, HeapCreateOptions.none, 0); 26 | if (!heap.isNull) 27 | { 28 | defaultHeap = heap; 29 | } 30 | } 31 | } 32 | } 33 | return defaultHeap; 34 | } 35 | private Handle getDefaultHeap() 36 | { 37 | auto heap = tryGetDefaultHeap(); 38 | enforce(!heap.isNull, "HeapCreate failed, error is ", GetLastError()); 39 | return heap; 40 | } 41 | 42 | void* malloc(size_t size) 43 | { 44 | auto heap = tryGetDefaultHeap(); 45 | if (heap.isNull) 46 | return null; 47 | return HeapAlloc(heap, HeapAllocOptions.none, size); 48 | } 49 | void free(void* mem) 50 | { 51 | auto result = HeapFree(getDefaultHeap(), HeapFreeOptions.none, mem); 52 | enforce(result.passed, "HeapFree failed, error is ", GetLastError()); 53 | } 54 | bool tryRealloc(void* mem, size_t size) 55 | { 56 | assert(0, "tryRealloc no impl"); 57 | } -------------------------------------------------------------------------------- /src/mar/mmap.d: -------------------------------------------------------------------------------- 1 | module mar.mmap; 2 | 3 | version (linux) 4 | { 5 | public import mar.linux.mmap; 6 | } 7 | else version (Windows) 8 | { 9 | public import mar.windows.mmap; 10 | } 11 | else static assert(0, "unsupported platform"); -------------------------------------------------------------------------------- /src/mar/octal.d: -------------------------------------------------------------------------------- 1 | module mar.octal; 2 | 3 | template octal(string literal) 4 | { 5 | version (D_BetterC) 6 | { 7 | static assert(0, "this octal template is not supported in betterC right now"); 8 | /* 9 | enum octal = (){ 10 | return cast(string)octalToHex(literal); 11 | }(); 12 | */ 13 | } 14 | else 15 | { 16 | enum octal = mixin(octalToHex(literal)); 17 | } 18 | } 19 | 20 | ubyte octalCharToValue(char c) 21 | in { assert(c <= '7' && c >= '0', "invalid octal digit"); } do 22 | { 23 | return cast(ubyte)(c - '0'); 24 | } 25 | private char toHexChar(T)(T value) 26 | in { assert(value <= 0xF, "value is too large to be a hex character"); } do 27 | { 28 | return cast(char)(value + ((value <= 9) ? '0' : ('A' - 10))); 29 | } 30 | 31 | 32 | version (D_BetterC) { } else 33 | { 34 | char[] octalToHex(string octLit) 35 | in { assert(octLit.length > 0); } do 36 | { 37 | auto hexDigitCount = 2 + (octLit.length + 1) * 3 / 4; 38 | auto hexLit = new char[hexDigitCount]; 39 | octalToHexImpl(hexLit, octLit.ptr, octLit.length); 40 | return hexLit; 41 | } 42 | } 43 | 44 | private void octalToHexImpl(char[] hexLit, const(char)* octLit, size_t octLength)//[] octLit) 45 | { 46 | size_t hexIndex = hexLit.length - 1; 47 | for (; octLength >= 4; octLength -= 4) 48 | { 49 | const _3 = octLit[octLength - 1].octalCharToValue; 50 | const _2 = octLit[octLength - 2].octalCharToValue; 51 | const _1 = octLit[octLength - 3].octalCharToValue; 52 | const _0 = octLit[octLength - 4].octalCharToValue; 53 | hexLit[hexIndex--] = toHexChar(((_2 & 0b001) << 3) | ((_3 ) )); 54 | hexLit[hexIndex--] = toHexChar(((_1 & 0b011) << 2) | ((_2 & 0b110) >> 1)); 55 | hexLit[hexIndex--] = toHexChar(((_0 ) << 1) | ((_1 & 0b100) >> 2)); 56 | } 57 | 58 | switch (octLength) 59 | { 60 | case 0: break; 61 | case 1: 62 | hexLit[hexIndex--] = toHexChar(octLit[0].octalCharToValue); 63 | break; 64 | case 2: 65 | { 66 | const temp = octLit[0].octalCharToValue; 67 | hexLit[hexIndex--] = toHexChar(((temp & 0b001) << 3) | octLit[1].octalCharToValue); 68 | hexLit[hexIndex--] = toHexChar( ((temp & 0b110) >> 1)); 69 | break; 70 | } 71 | case 3: 72 | { 73 | const temp0 = octLit[0].octalCharToValue; 74 | const temp1 = octLit[1].octalCharToValue; 75 | hexLit[hexIndex--] = toHexChar(((temp1 & 0b001) << 3) | octLit[2].octalCharToValue); 76 | hexLit[hexIndex--] = toHexChar(((temp0 & 0b011) << 2) | ((temp1 & 0b110) >> 1)); 77 | hexLit[hexIndex--] = toHexChar( ((temp0 & 0b100) >> 2)); 78 | break; 79 | } 80 | default: assert(0, "code bug"); 81 | } 82 | 83 | assert(hexIndex == 1, "code bug"); 84 | hexLit[1] = 'x'; 85 | hexLit[0] = '0'; 86 | } 87 | 88 | version (D_BetterC) { } else 89 | unittest 90 | { 91 | assert("0x0" == "0".octalToHex); 92 | assert("0x1" == "1".octalToHex); 93 | assert("0x0" == "0".octalToHex); 94 | assert("0x7" == "7".octalToHex); 95 | assert("0x08" == "10".octalToHex); 96 | assert("0x38" == "70".octalToHex); 97 | assert("0x1C0" == "700".octalToHex); 98 | assert("0xFFF" == "7777".octalToHex); 99 | assert("0x053977" == "1234567".octalToHex); 100 | assert("0x0000A" == "000012".octalToHex); 101 | } 102 | 103 | 104 | template octalDec(alias decimalValue) 105 | { 106 | enum octalDec = decimalToOctal(decimalValue); 107 | } 108 | 109 | // take a value written using decimal digits and interpret 110 | // it as if it was written in octal. 111 | auto decimalToOctal(T)(T value) 112 | { 113 | T result = 0; 114 | ushort shift = 0; 115 | for (; value > 0; value /= 10) 116 | { 117 | auto mod10 = value % 10; 118 | if (mod10 > 7) 119 | assert(0, "decimal value contains non-octal digits"); 120 | result |= (mod10 << shift); 121 | shift += 3; 122 | } 123 | return result; 124 | } 125 | 126 | unittest 127 | { 128 | assert(0b000_000 == decimalToOctal(00)); 129 | assert(0b000_111 == decimalToOctal(07)); 130 | assert(0b001_000 == decimalToOctal(10)); 131 | assert(0b001_111 == decimalToOctal(17)); 132 | assert(0b010_000 == decimalToOctal(20)); 133 | assert(0b010_111 == decimalToOctal(27)); 134 | } 135 | 136 | /** 137 | Using a hex literal, get the value interpreting it as if it is an octal litera. 138 | Example: 139 | --- 140 | assert(01234 == octalHex!01234); 141 | --- 142 | */ 143 | template octalHex(alias hexValue) 144 | { 145 | enum octalHex = hexToOctal(hexValue); 146 | } 147 | 148 | // take a value written using decimal digits and interpret 149 | // it as if it was written in octal. 150 | auto hexToOctal(T)(T value) 151 | { 152 | T result = 0; 153 | ushort shift = 0; 154 | for (; value > 0; value >>= 4) 155 | { 156 | auto mod = value & 0b1111; 157 | if (mod > 7) 158 | assert(0, "hex value contains non-octal digits"); 159 | result |= (mod << shift); 160 | shift += 3; 161 | } 162 | return result; 163 | } 164 | 165 | unittest 166 | { 167 | assert(0b000_000 == hexToOctal(0x00)); 168 | assert(0b000_111 == hexToOctal(0x07)); 169 | assert(0b001_000 == hexToOctal(0x10)); 170 | assert(0b001_111 == hexToOctal(0x17)); 171 | assert(0b010_000 == hexToOctal(0x20)); 172 | assert(0b010_111 == hexToOctal(0x27)); 173 | } 174 | -------------------------------------------------------------------------------- /src/mar/passfail.d: -------------------------------------------------------------------------------- 1 | module mar.passfail; 2 | 3 | // Example: 4 | // --- 5 | // return boolstatus.pass; 6 | // return boolstatus.fail; 7 | // if(status.failed) ... 8 | // if(status.passed) ... 9 | struct passfail 10 | { 11 | private bool _passed; 12 | @property static passfail pass() pure nothrow @nogc { return passfail(true); } 13 | @property static passfail fail() pure nothrow @nogc { return passfail(false); } 14 | @disable this(); 15 | private this(bool _passed) pure nothrow @nogc { this._passed = _passed; } 16 | /* 17 | @property bool asBool() { pragma(inline, true); return _passed; } 18 | alias asBool this; 19 | */ 20 | auto print(P)(P printer) { return printer.put(_passed ? "pass" : "fail"); } 21 | 22 | @property auto passed() const pure nothrow @nogc { return _passed; } 23 | @property auto failed() const pure nothrow @nogc { return !_passed; } 24 | // TODO: file/line number also? 25 | void enforce(E...)(E errorMsgValues) const 26 | { 27 | static import mar.enforce; 28 | mar.enforce.enforce(this, errorMsgValues); 29 | } 30 | 31 | passfail opBinary(string op)(const(passfail) right) const pure nothrow @nogc 32 | { 33 | mixin("return passfail(this._passed " ~ op ~ " right._passed);"); 34 | } 35 | passfail opBinary(string op)(const(bool) right) const pure nothrow @nogc 36 | { 37 | mixin("return passfail(this._passed " ~ op ~ " right);"); 38 | } 39 | passfail opBinaryRight(string op)(const(bool) left) const pure nothrow @nogc 40 | { 41 | mixin("return passfail(left " ~ op ~ " this._passed);"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/mar/path.d: -------------------------------------------------------------------------------- 1 | module mar.path; 2 | 3 | auto baseNameSplitIndex(T)(T path) 4 | { 5 | size_t i = path.length; 6 | for (; i > 0;) 7 | { 8 | i--; 9 | if (path[i] == '/') 10 | { 11 | i++; 12 | break; 13 | } 14 | } 15 | return i; 16 | } 17 | 18 | auto dirName(T)(T path) 19 | { 20 | return path[0 .. baseNameSplitIndex(path)]; 21 | } 22 | auto baseName(T)(T path) 23 | { 24 | return path[baseNameSplitIndex(path) .. $]; 25 | } 26 | 27 | /** 28 | Example: 29 | --- 30 | foreach (dir; SubPathIterator("/foo/bar//baz/bon")) 31 | { 32 | stdout.writeln(dir); 33 | } 34 | /* 35 | prints: 36 | /foo/ 37 | /foo/bar// 38 | /foo/bar//baz/ 39 | /foo/bar//baz/bon 40 | --- 41 | */ 42 | struct SubPathIterator 43 | { 44 | static bool isValidStartIndex(const(char)[] dir, size_t index) 45 | { 46 | return 47 | index == 0 || 48 | index == dir.length || 49 | (dir[index - 1] == '/' && dir[index] != '/'); 50 | } 51 | 52 | const(char)[] current; 53 | size_t limit; 54 | 55 | /** 56 | start must point to 57 | 1) 0 58 | 2) dir.length 59 | 3) after a group of directory separators 60 | Examples: valid start offsets marked with * 61 | --- 62 | foo/bar//baz 63 | * * * * 64 | 0 4 9 12 65 | 66 | /apple/tree 67 | ** * 68 | 01 7 69 | --- 70 | */ 71 | this(const(char)[] dir, size_t start = 0) 72 | in { assert(isValidStartIndex(dir, start), "codebug: SubPathIterator, invalid start index"); } do 73 | { 74 | this.current = dir[0 .. start]; 75 | this.limit = dir.length; 76 | popFront(); 77 | } 78 | bool empty() const { return current is null; } 79 | auto front() const { return current; } 80 | void popFront() 81 | { 82 | auto next = current.length; 83 | if (next == limit) 84 | { 85 | current = null; 86 | return; 87 | } 88 | 89 | for (;;) 90 | { 91 | if (current.ptr[next] == '/') 92 | { 93 | // skip extra '/' 94 | for(;;) 95 | { 96 | next++; 97 | if (next == limit || current.ptr[next] != '/') 98 | break; 99 | } 100 | break; 101 | } 102 | next++; 103 | if (next >= limit) 104 | break; 105 | } 106 | current = current.ptr[0 .. next]; 107 | } 108 | } 109 | 110 | unittest 111 | { 112 | static void test(string path, string[] dirs...) 113 | { 114 | size_t dirIndex = 0; 115 | foreach (dir; SubPathIterator(path)) 116 | { 117 | assert(dir == dirs[dirIndex]); 118 | dirIndex++; 119 | } 120 | } 121 | test("/foo/bar//baz/bon", "/", "/foo/", "/foo/bar//", "/foo/bar//baz/", "/foo/bar//baz/bon"); 122 | 123 | test("", null); 124 | test("/", "/"); 125 | test("a", "a"); 126 | test("//", "//"); 127 | test("/a", "/", "/a"); 128 | test("/a/", "/", "/a/"); 129 | test("//a", "//", "//a"); 130 | test("//a/", "//", "//a/"); 131 | test("//a//", "//", "//a//"); 132 | test("///a///", "///", "///a///"); 133 | test("///foo///", "///", "///foo///"); 134 | test("///foo///bar", "///", "///foo///", "///foo///bar"); 135 | test("///foo///bar/", "///", "///foo///", "///foo///bar/"); 136 | test("///foo///bar//", "///", "///foo///", "///foo///bar//"); 137 | } 138 | -------------------------------------------------------------------------------- /src/mar/print/printers.d: -------------------------------------------------------------------------------- 1 | module mar.print.printers; 2 | 3 | // TODO: make one for bigint? 4 | // TODO: detect size_t overflow? 5 | struct CalculateSizePrinterSizet 6 | { 7 | import mar.cannotfail; 8 | 9 | private size_t size; 10 | // 11 | // The IsCalculateSizePrinter Interface 12 | // 13 | auto getSize() const { return size; } 14 | auto addSize(size_t size) { this.size += size; } 15 | // 16 | // The Printer Interface 17 | // 18 | enum IsPrinter = true; 19 | enum IsCalculateSizePrinter = true; 20 | alias PutResult = CannotFail; 21 | static PutResult success() { return CannotFail(); } 22 | PutResult flush() { pragma(inline, true); return CannotFail(); } 23 | PutResult put(const(char)[] str) { size += str.length; return CannotFail(); } 24 | PutResult putc(const char c) { size++; return CannotFail(); } 25 | } 26 | 27 | struct DefaultPrinterBuffer 28 | { 29 | char* ptr; 30 | auto commitValue() 31 | { 32 | return this; 33 | } 34 | void putc(char c) { pragma(inline, true); ptr[0] = c; ptr++; } 35 | } 36 | 37 | /** 38 | Prints a set of values into a given character array. 39 | If the character array is too small, it asserts. 40 | TODO: support ability to return failure? 41 | */ 42 | struct FixedSizeStringPrinter 43 | { 44 | import mar.array : acopy; 45 | import mar.cannotfail; 46 | 47 | private char[] buffer; 48 | private size_t bufferedLength; 49 | 50 | auto getLength() const { return bufferedLength; } 51 | private auto capacity() const { return buffer.length - bufferedLength; } 52 | private void enforceCapacity(size_t needed) 53 | { 54 | assert(capacity >= needed, "StringPrinter capacity is too small"); 55 | } 56 | 57 | // 58 | // The Printer Interface 59 | // 60 | enum IsPrinter = true; 61 | enum IsCalculateSizePrinter = false; 62 | alias PutResult = CannotFail; 63 | static PutResult success() { return CannotFail(); } 64 | 65 | PutResult flush() { pragma(inline, true); return success; } 66 | PutResult put(const(char)[] str) 67 | { 68 | enforceCapacity(str.length); 69 | acopy(buffer.ptr + bufferedLength, str); 70 | bufferedLength += str.length; 71 | return success; 72 | } 73 | PutResult putc(const char c) 74 | { 75 | enforceCapacity(1); 76 | buffer[bufferedLength++] = c; 77 | return success; 78 | } 79 | 80 | auto getTempBuffer(size_t size)() 81 | { 82 | pragma(inline, true); 83 | 84 | enforceCapacity(size); 85 | return DefaultPrinterBuffer(buffer.ptr + + bufferedLength); 86 | } 87 | /+ 88 | auto tryGetTempBufferImpl(size_t size) 89 | { 90 | return (capacity < size) ? 91 | DefaultPrinterBuffer(null) : 92 | DefaultPrinterBuffer(buffer.ptr + bufferedLength); 93 | } 94 | +/ 95 | void commitBuffer(DefaultPrinterBuffer buf) 96 | { 97 | bufferedLength = buf.ptr - buffer.ptr; 98 | } 99 | } 100 | 101 | // Every printer should be able to return a buffer 102 | // large enough to hold at least some characters for 103 | // things like printing numbers and such 104 | //enum MinPrinterBufferLength = 30; ?? 105 | 106 | struct BufferedFileReturnErrorPrinterPolicy 107 | { 108 | import mar.file : FileD; 109 | import mar.expect; 110 | 111 | enum bufferLength = 50; 112 | 113 | mixin ExpectMixin!("PutResult", void, 114 | ErrorCase!("writeFailed", "write failed, returned %", ptrdiff_t)); 115 | 116 | static PutResult success() { pragma(inline, true); return PutResult.success; } 117 | static PutResult writeFailed(FileD dest, size_t writeSize, ptrdiff_t errno) 118 | { 119 | // TODO: do something with dest/writeSize 120 | return PutResult.writeFailed(errno); 121 | } 122 | } 123 | version (NoExit) 124 | alias DefaultBufferedFilePrinterPolicy = BufferedFileReturnErrorPrinterPolicy; 125 | else 126 | { 127 | alias DefaultBufferedFilePrinterPolicy = BufferedFileExitPrinterPolicy; 128 | struct BufferedFileExitPrinterPolicy 129 | { 130 | import mar.cannotfail; 131 | import mar.file : FileD; 132 | 133 | enum bufferLength = 50; 134 | 135 | alias PutResult = CannotFail; 136 | static CannotFail success() { return CannotFail(); } 137 | static CannotFail writeFailed(FileD dest, size_t writeSize, ptrdiff_t returnValue) 138 | { 139 | import mar.stdio : stderr; 140 | import mar.process : exit; 141 | stderr.write("Error: write failed! (TODO: print error writeSize and returnValue)"); 142 | exit(1); 143 | assert(0); 144 | } 145 | } 146 | } 147 | 148 | struct BufferedFilePrinter(Policy) 149 | { 150 | import mar.array : acopy; 151 | import mar.file : FileD; 152 | 153 | static assert(__traits(hasMember, Policy, "PutResult")); 154 | static assert(__traits(hasMember, Policy, "success")); 155 | static assert(__traits(hasMember, Policy, "writeFailed")); 156 | static assert(__traits(hasMember, Policy, "bufferLength")); 157 | 158 | private FileD fd; 159 | private char* buffer; 160 | private size_t bufferedLength; 161 | 162 | // 163 | // The Printer Interface 164 | // 165 | enum IsPrinter = true; 166 | enum IsCalculateSizePrinter = false; 167 | alias PutResult = Policy.PutResult; 168 | static PutResult success() { return Policy.success; } 169 | PutResult flush() 170 | { 171 | if (bufferedLength > 0) 172 | { 173 | auto result = fd.tryWrite(buffer, bufferedLength); 174 | if (result.failed) 175 | return Policy.writeFailed(fd, bufferedLength, result.errorCode/*, result.onFailWritten*/); 176 | bufferedLength = 0; 177 | } 178 | return success; 179 | } 180 | PutResult put(const(char)[] str) 181 | { 182 | auto left = Policy.bufferLength - bufferedLength; 183 | if (left < str.length) 184 | { 185 | { 186 | auto result = flush(); 187 | if (result.failed) 188 | return result; 189 | } 190 | { 191 | auto result = fd.tryWrite(str); 192 | if (result.failed) 193 | return Policy.writeFailed(fd, str.length, result.errorCode/*, result.onFailWritten*/); 194 | } 195 | } 196 | else 197 | { 198 | acopy(buffer + bufferedLength, str); 199 | bufferedLength += str.length; 200 | } 201 | return success; 202 | } 203 | PutResult putc(const char c) 204 | { 205 | if (bufferedLength == Policy.bufferLength) 206 | { 207 | auto result = flush(); 208 | if (result.failed) 209 | return result; 210 | } 211 | buffer[bufferedLength++] = c; 212 | return success; 213 | } 214 | 215 | /** 216 | Example: 217 | --- 218 | auto buffer = printer.getTempBuffer(10); 219 | buffer.ptr[0] = 'a'; 220 | buffer.ptr[1] = 'b'; 221 | printer.commitBuffer(buffer.commitValue); 222 | --- 223 | WARNING: you cannot call other printer functions while 224 | using this buffer, like `flush`, `put`. 225 | */ 226 | auto getTempBuffer(size_t size)() 227 | { 228 | pragma(inline, true); 229 | static assert(size <= Policy.bufferLength); 230 | auto left = Policy.bufferLength - bufferedLength; 231 | if (left < size) 232 | flush(); 233 | return DefaultPrinterBuffer(buffer + bufferedLength); 234 | } 235 | /+ 236 | auto tryGetTempBufferImpl(size_t size) 237 | { 238 | if (size > Policy.bufferLength) 239 | return DefaultPrinterBuffer(null); // can't get a buffer of that size 240 | auto left = Policy.bufferLength - bufferedLength; 241 | if (left < size) 242 | flush(); 243 | return DefaultPrinterBuffer(buffer + bufferedLength); 244 | } 245 | +/ 246 | void commitBuffer(DefaultPrinterBuffer buf) 247 | { 248 | bufferedLength = buf.ptr - buffer; 249 | } 250 | } 251 | 252 | -------------------------------------------------------------------------------- /src/mar/print/sprint.d: -------------------------------------------------------------------------------- 1 | module mar.print.sprint; 2 | 3 | import mar.sentinel : SentinelArray; 4 | 5 | version (NoExit) { } else 6 | { 7 | /** 8 | Does not return errors, asserts if the given `buffer` is too small. This means 9 | that these functions are not available if NoExit is specified. 10 | 11 | The "Sentinel" variants will append a terminating null to the end of the 12 | character buffer, but return a size or array length not including the terminating null. 13 | 14 | The "JustReturnSize" variants will just return the size of the printed string (not including 15 | the terminating null) with the rest returning the given buffer with the length of the 16 | printed string. 17 | */ 18 | char[] sprint(T...)(char[] buffer, T args) 19 | { 20 | pragma(inline, true); 21 | return buffer[0 .. sprintJustReturnSize(buffer, args)]; 22 | } 23 | /// ditto 24 | SentinelArray!char sprintSentinel(T...)(char[] buffer, T args) 25 | { 26 | pragma(inline, true); 27 | import mar.sentinel : assumeSentinel; 28 | return buffer[0 .. sprintSentinelJustReturnSize(buffer, args)].assumeSentinel; 29 | } 30 | /// ditto 31 | size_t sprintJustReturnSize(T...)(char[] buffer, T args) 32 | { 33 | import mar.print : printArgs; 34 | import mar.print.printers : FixedSizeStringPrinter; 35 | 36 | auto printer = FixedSizeStringPrinter(buffer, 0); 37 | printArgs(&printer, args); 38 | return printer.getLength; 39 | } 40 | /// ditto 41 | size_t sprintSentinelJustReturnSize(T...)(char[] buffer, T args) 42 | { 43 | import mar.print : printArgs; 44 | import mar.print.printers : FixedSizeStringPrinter; 45 | 46 | auto printer = FixedSizeStringPrinter(buffer, 0); 47 | printArgs(&printer, args, '\0'); 48 | return printer.getLength - 1; 49 | } 50 | 51 | /** 52 | These functions will pre-calculate the size needed to create a string with 53 | the given arguments, allocate then memory for it, then print the string 54 | and return it. 55 | 56 | The "Sentinel" variant will append a terminating null whereas the "NoSentinel" will not. 57 | The length of the array returned by the "Sentinel" variant will not include the terminating null. 58 | 59 | These functions will assert on error, they will never return null. 60 | This is the simpler versions of the functions where they assert if the pre-calulated size 61 | does not match the actual printed size. Since it asserts in this case, we also decide to 62 | assert when malloc fails. 63 | (TODO: will probably create variants that return an error instead). 64 | 65 | */ 66 | char[] sprintMallocNoSentinel(T...)(T args) 67 | { 68 | import mar.mem : malloc; 69 | import mar.print : getPrintSize; 70 | 71 | auto totalSize = getPrintSize(args); 72 | auto buffer = cast(char*)malloc(totalSize); 73 | if (!buffer) 74 | { 75 | // might as well assert, because we have to assert when printedSize != totalSize 76 | assert(0, "malloc failed"); 77 | } 78 | const printedSize = sprintJustReturnSize(buffer[0 .. totalSize], args); 79 | assert(printedSize == totalSize, "codebug: precalculated print size differed from actual size"); 80 | return buffer[0 .. totalSize]; 81 | } 82 | /// ditto 83 | SentinelArray!char sprintMallocSentinel(T...)(T args) 84 | { 85 | import mar.mem : malloc; 86 | import mar.sentinel : assumeSentinel; 87 | import mar.print : getPrintSize; 88 | 89 | const totalSize = getPrintSize(args); 90 | auto buffer = cast(char*)malloc(totalSize + 1); 91 | if (!buffer) 92 | { 93 | // might as well assert, because we have to assert when printedSize != totalSize 94 | assert(0, "malloc failed"); 95 | } 96 | const printedSize = sprintSentinelJustReturnSize(buffer[0 .. totalSize + 1], args); 97 | assert(printedSize == totalSize, "codebug: precalculated print size differed from actual size"); 98 | return buffer[0 .. totalSize].assumeSentinel; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/mar/process.d: -------------------------------------------------------------------------------- 1 | module mar.process; 2 | 3 | import mar.expect; 4 | 5 | version (linux) 6 | { 7 | static import mar.linux.process; 8 | import mar.linux.process : pid_t; 9 | alias wait = mar.linux.process.wait; 10 | } 11 | 12 | version (NoExit) {} else 13 | { 14 | version (linux) 15 | { 16 | alias exit = mar.linux.process.exit; 17 | } 18 | else version (Windows) 19 | { 20 | void exit(uint exitCode) 21 | { 22 | pragma(inline, true); 23 | import mar.windows.kernel32 : ExitProcess; 24 | ExitProcess(exitCode); 25 | } 26 | } 27 | } 28 | 29 | version (linux) 30 | alias ProcID = pid_t; 31 | else version (Windows) 32 | alias ProcID = int; // TODO: this is not right 33 | else static assert(0, "unsupported platform"); 34 | 35 | /** 36 | TODO: create api for what environment variables to pass 37 | */ 38 | struct ProcBuilder 39 | { 40 | import mar.arraybuilder; 41 | import mar.sentinel : SentinelPtr, SentinelArray, assumeSentinel; 42 | import mar.c : cstring; 43 | version (Posix) 44 | { 45 | import mar.linux.process : fork, execve; 46 | } 47 | 48 | static ProcBuilder forExeFile(cstring programFile) 49 | { 50 | return ProcBuilder(programFile); 51 | } 52 | static ProcBuilder forExeFile(SentinelArray!(const(char)) programFile) 53 | { 54 | return ProcBuilder(programFile); 55 | } 56 | 57 | version (Windows) 58 | { 59 | private SentinelArray!(immutable(char)) program; 60 | ArrayBuilder!(char, MallocArrayBuilderPolicy!300) args; 61 | } 62 | else version (Posix) 63 | { 64 | ArrayBuilder!(cstring, MallocArrayBuilderPolicy!40) args; 65 | } 66 | @disable this(); 67 | private this(cstring program) 68 | { 69 | version (Windows) 70 | { 71 | assert (0, "mar.process ProcBuilder.ctor:cstring not impl"); 72 | //this.program = program.walkToArray; 73 | } 74 | else version (Posix) 75 | assert(args.tryPut(program).passed, "out of memory"); 76 | } 77 | private this(SentinelArray!(const(char)) program) 78 | { 79 | version (Windows) 80 | { 81 | assert (0, "mar.process ProcBuilder.ctor:SentinelArray not impl"); 82 | //this.program = program; 83 | } 84 | else version (Posix) 85 | assert(args.tryPut(program.ptr).passed, "out of memory"); 86 | } 87 | 88 | auto tryPut(cstring arg) 89 | { 90 | version (Windows) 91 | { 92 | // TODO: append to args, make sure it is escaped if necessary 93 | assert (0, "mar.process ProcBuilder.tryPut:cstring not impl"); 94 | import mar.passfail; 95 | return passfail.fail; 96 | } 97 | else version (Posix) 98 | return args.tryPut(arg); 99 | } 100 | 101 | version (Windows) 102 | { 103 | auto tryPut(SentinelArray!(const(char)) arg) 104 | { 105 | assert (0, "mar.process ProcBuilder.tryPut:SentinelArray not impl"); 106 | import mar.passfail; 107 | return passfail.fail; 108 | } 109 | } 110 | else version (Posix) 111 | { 112 | auto tryPut(SentinelArray!(const(char)) arg) 113 | { 114 | pragma(inline, true); 115 | return tryPut(arg.ptr); 116 | } 117 | } 118 | 119 | mixin ExpectMixin!("StartResult", ProcID, 120 | ErrorCase!("outOfMemory", "out of memory"), 121 | ErrorCase!("forkFailed", "fork failed, returned %", ptrdiff_t)); 122 | 123 | /** free memory for arguments */ 124 | void free() 125 | { 126 | version (Windows) 127 | args.free(); 128 | else version (Posix) 129 | args.free(); 130 | } 131 | 132 | /** Start the process and clean the data structures created to start the process */ 133 | StartResult startWithClean(SentinelPtr!cstring envp) 134 | { 135 | auto result = startImpl(envp); 136 | free(); 137 | return result; 138 | } 139 | private StartResult startImpl(SentinelPtr!cstring envp) 140 | { 141 | import mar.enforce; 142 | 143 | version (Windows) 144 | { 145 | assert (0, "mar.process ProcBuilder.startImpl not impl"); 146 | } 147 | else version (Posix) 148 | { 149 | if (tryPut(cstring.nullValue).failed) 150 | return StartResult.outOfMemory(); 151 | 152 | // TODO: maybe I'm supposed to use posix_spawn instead of fork/exec? 153 | auto pidResult = fork(); 154 | if (pidResult.failed) 155 | return StartResult.forkFailed(pidResult.numval); 156 | if (pidResult.val == 0) 157 | { 158 | auto result = execve(args.data[0], args.data.ptr.assumeSentinel, envp); 159 | // TODO: how do we handle this error in the new process? 160 | enforce(false, "execve failed"); 161 | //exit( (result.numval == 0) ? 1 : result.numval); 162 | } 163 | return StartResult.success(pidResult.val); 164 | } 165 | } 166 | 167 | auto print(P)(P printer) const 168 | { 169 | version (Windows) 170 | { 171 | assert(0, "not impl"); 172 | return P.success; 173 | } 174 | else version (Posix) 175 | { 176 | // TODO: how to handle args with spaces? 177 | { 178 | auto result = printer.put(args.data[0].walkToArray.array); 179 | if (result.failed) 180 | return result; 181 | } 182 | foreach (arg; args.data[1 .. $]) 183 | { 184 | { 185 | auto result = printer.putc(' '); 186 | if (result.failed) 187 | return result; 188 | } 189 | { 190 | auto result = printer.put(arg.walkToArray.array); 191 | if (result.failed) 192 | return result; 193 | } 194 | } 195 | return P.success; 196 | } 197 | } 198 | } 199 | 200 | unittest 201 | { 202 | import mar.sentinel; 203 | import mar.c : cstring; 204 | 205 | { 206 | auto proc = ProcBuilder.forExeFile(lit!"/usr/bin/env"); 207 | auto startResult = proc.startWithClean(SentinelPtr!cstring.nullValue); 208 | if (startResult.failed) 209 | { 210 | import mar.stdio; stdout.write("proc start failed: %s", startResult); 211 | } 212 | else 213 | { 214 | version (posix) 215 | { 216 | import mar.linux.process : wait; 217 | import mar.stdio; stdout.write("started /usr/bin/env\n"); 218 | auto waitResult = wait(startResult.val); 219 | stdout.write("waitResult is ", waitResult, "\n"); 220 | assert(!waitResult.failed); 221 | } 222 | } 223 | } 224 | { 225 | auto proc = ProcBuilder.forExeFile(lit!"a"); 226 | assert(proc.tryPut(lit!"b").passed); 227 | assert(proc.tryPut(litPtr!"b").passed); 228 | import mar.stdio; stdout.writeln(proc); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/mar/qual.d: -------------------------------------------------------------------------------- 1 | module mar.qual; 2 | 3 | immutable(T)[] asImmutable(T)(T[] array) pure nothrow 4 | { 5 | pragma(inline, true); 6 | return .asImmutable(array); // call ref version 7 | } 8 | immutable(T)[] asImmutable(T)(ref T[] array) pure nothrow 9 | { 10 | pragma(inline, true); 11 | auto result = cast(immutable(T)[]) array; 12 | array = null; 13 | return result; 14 | } 15 | immutable(T[U]) asImmutable(T, U)(ref T[U] array) pure nothrow 16 | { 17 | pragma(inline, true); 18 | auto result = cast(immutable(T[U])) array; 19 | array = null; 20 | return result; 21 | } 22 | -------------------------------------------------------------------------------- /src/mar/serialize.d: -------------------------------------------------------------------------------- 1 | module mar.serialize; 2 | 3 | T deserializeBE(T, U)(U* bytes) if (U.sizeof == 1) 4 | { 5 | pragma(inline, true); 6 | version (BigEndian) 7 | return deserializeInOrder!T(bytes); 8 | else 9 | return deserializeSwap!T(bytes); 10 | } 11 | T deserializeLE(T, U)(U* bytes) if (U.sizeof == 1) 12 | { 13 | pragma(inline, true); 14 | version (BigEndian) 15 | return deserializeSwap!T(bytes); 16 | else 17 | return deserializeInOrder!T(bytes); 18 | } 19 | /* 20 | T deserialize(T, bool bigEndian, U)(U* bytes) if (U.sizeof == 1) 21 | { 22 | static if (T.sizeof == 1) 23 | { 24 | pragma(inline, true); 25 | return cast(T)bytes[0]; 26 | } 27 | else static if (T.sizeof == 2) 28 | { 29 | return cast(T)( 30 | cast(ushort)bytes[EndianIndex!(bigEndian, T.sizeof, 0)] << 0 | 31 | cast(ushort)bytes[EndianIndex!(bigEndian, T.sizeof, 1)] << 8 ); 32 | } 33 | else static if (T.sizeof == 4) 34 | { 35 | return cast(T)( 36 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 0)] << 0 | 37 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 1)] << 8 | 38 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 2)] << 16 | 39 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 3)] << 24 ); 40 | } 41 | else static if (T.sizeof == 8) 42 | { 43 | return cast(T)( 44 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 0)] << 0 | 45 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 1)] << 8 | 46 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 2)] << 16 | 47 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 3)] << 24 | 48 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 4)] << 32 | 49 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 5)] << 40 | 50 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 6)] << 48 | 51 | cast(uint)bytes[EndianIndex!(bigEndian, T.sizeof, 7)] << 56 ); 52 | } 53 | else static assert(0, "don't know how to deserialize a type of this size"); 54 | } 55 | */ 56 | 57 | T deserializeInOrder(T, U)(U* bytes) if (U.sizeof == 1 && !is(U == ubyte)) 58 | { 59 | pragma(inline, true); 60 | return deserializeInOrder!T(cast(ubyte*)bytes); 61 | } 62 | T deserializeInOrder(T)(ubyte* bytes) 63 | { 64 | pragma(inline, true); 65 | import mar.mem : memcpy; 66 | 67 | T value; 68 | memcpy(&value, bytes, T.sizeof); 69 | return value; 70 | } 71 | 72 | T deserializeSwap(T, U)(U* bytes) if (U.sizeof == 1 && !is(U == ubyte)) 73 | { 74 | pragma(inline, true); 75 | return deserializeSwap!T(cast(ubyte*)bytes); 76 | } 77 | T deserializeSwap(T)(ubyte* bytes) 78 | { 79 | T value; 80 | auto dst = cast(ubyte*)&value; 81 | foreach (i; 0 .. T.sizeof) 82 | { 83 | dst[i] = bytes[T.sizeof - 1 - i]; 84 | } 85 | return value; 86 | } 87 | -------------------------------------------------------------------------------- /src/mar/start.d: -------------------------------------------------------------------------------- 1 | /** 2 | Resource: https://wiki.osdev.org/Calling_Conventions 3 | */ 4 | module mar.start; 5 | 6 | version (linux) 7 | { 8 | version (X86_64) 9 | { 10 | enum startMixin = q{ 11 | /** 12 | STACK (Low to High) 13 | ------------------------------ 14 | RSP --> | argc | 15 | argv --> | argv[0] | 16 | | argv[1] | 17 | | ... | 18 | | argv[argc] (NULL) | 19 | envp --> | envp[0] | 20 | | envp[1] | 21 | | ... | 22 | | (NULL) | 23 | */ 24 | extern (C) void _start() 25 | { 26 | asm 27 | { 28 | naked; 29 | xor RBP,RBP; // zero the frame pointer register 30 | // I think this helps backtraces know the call stack is over 31 | // 32 | // set argc 33 | // 34 | pop RDI; // RDI(first arg to 'main') = argc 35 | // 36 | // set argv 37 | // 38 | mov RSI,RSP; // RSI(second arg to 'main) = argv (pointer to stack) 39 | // 40 | // set envp 41 | // 42 | mov RDX,RDI; // first put the argc count into RDX (where envp will go) 43 | add RDX,1; // add 1 to value from argc (handle one NULL pointer after argv) 44 | shl RDX, 3; // multiple argc by 8 (get offset of envp) 45 | add RDX,RSP; // offset this value from the current stack pointer 46 | // 47 | // prepare stack for main 48 | // 49 | add RSP,-8; // move stack pointer below argc 50 | and SPL, 0xF8; // align stack pointer on 8-byte boundary 51 | call main; 52 | // 53 | // exit syscall 54 | // 55 | mov RDI, RAX; // syscall param 1 = RAX (return value of main) 56 | mov RAX, 60; // SYS_exit 57 | syscall; 58 | } 59 | } 60 | }; 61 | } 62 | else static assert(0, "start not implemented on linux for this processor"); 63 | } 64 | else version (Windows) 65 | { 66 | /* 67 | version (X86) 68 | { 69 | static assert(0, "TODO: implement start on windows x86!!!!"); 70 | } 71 | else static assert(0, "start not implemented on windows for this processor"); 72 | */ 73 | enum startMixin = q{ 74 | enum STD_OUTPUT_HANDLE = 0xFFFFFFF5; 75 | struct HANDLE { uint val; } 76 | extern (Windows) HANDLE GetStdHandle(uint stdHandle); 77 | extern (Windows) int WriteFile(HANDLE file, const(char)* data, uint length, uint* written, void* overlapped = null); 78 | extern (Windows) int mainCRTStartup() 79 | { 80 | auto result = GetStdHandle(STD_OUTPUT_HANDLE); 81 | uint written; 82 | WriteFile(result, "Hello!".ptr, 6, &written); 83 | return 0; 84 | } 85 | }; 86 | } 87 | else static assert(0, "start not implemented for this platform"); 88 | -------------------------------------------------------------------------------- /src/mar/stdio.d: -------------------------------------------------------------------------------- 1 | module mar.stdio; 2 | 3 | version (linux) 4 | { 5 | public import mar.linux.stdio; 6 | } 7 | else version (Windows) 8 | { 9 | public import mar.windows.stdio; 10 | } 11 | else static assert(0, __MODULE__ ~ " is not supported on this platform"); 12 | -------------------------------------------------------------------------------- /src/mar/string.d: -------------------------------------------------------------------------------- 1 | module mar.string; 2 | 3 | import mar.c : cstring; 4 | 5 | template isStringLike(T) 6 | { 7 | import mar.array : isArrayLike; 8 | 9 | static if (isArrayLike!T) 10 | enum isStringLike = (T.init[0].sizeof == 1); 11 | else 12 | enum isStringLike = false; 13 | } 14 | 15 | version (NoStdc) 16 | { 17 | } 18 | else 19 | { 20 | static import core.stdc.string; 21 | } 22 | 23 | size_t strlen(cstring str) 24 | { 25 | version (NoStdc) 26 | { 27 | for (size_t i = 0; ;i++) 28 | { 29 | if (str.raw[i] == '\0') 30 | return i; 31 | } 32 | } 33 | else 34 | return core.stdc.string.strlen(str.raw); 35 | } 36 | -------------------------------------------------------------------------------- /src/mar/sync.d: -------------------------------------------------------------------------------- 1 | module mar.sync; 2 | 3 | struct CriticalSection 4 | { 5 | version (linux) 6 | static assert(0, "not implemented"); 7 | version (Windows) 8 | { 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /src/mar/thread.d: -------------------------------------------------------------------------------- 1 | /** 2 | Platform-agnostic thread API 3 | */ 4 | module mar.thread; 5 | 6 | import mar.expect : ExpectMixin, ErrorCase; 7 | 8 | version (Windows) 9 | { 10 | import mar.windows : Handle; 11 | static import mar.windows; 12 | 13 | alias ThreadEntry = mar.windows.ThreadEntry; 14 | mixin ExpectMixin!("StartThreadResult", Handle, 15 | ErrorCase!("createThreadFailed", "CreateThread failed, e=%", uint)); 16 | enum ThreadEntryResult : uint 17 | { 18 | fail = 0, // error is zero 19 | pass = 1, // success is non-zero 20 | } 21 | } 22 | else static assert("mar.thread not implemented on this platform"); 23 | 24 | /** 25 | Usage: 26 | --- 27 | mixin threadEntryMixin!("myThread", q{ 28 | // thread code 29 | }); 30 | --- 31 | */ 32 | mixin template threadEntryMixin(string name, string body_) 33 | { 34 | version (Windows) 35 | enum signature = "extern (Windows) uint " ~ name ~ "(void* threadArg)"; 36 | else static assert("mar.thread.threadEntryMixin not implemented on this platform"); 37 | 38 | mixin(signature ~ "\n{\n" ~ body_ ~ "\n}\n"); 39 | } 40 | 41 | StartThreadResult startThread(ThreadEntry entry) 42 | { 43 | version (Windows) 44 | { 45 | pragma(inline, true); 46 | 47 | import mar.windows.kernel32 : GetLastError, CreateThread; 48 | const handle = CreateThread(null, 0, entry, null, 0, null); 49 | if (handle.isNull) 50 | return StartThreadResult.createThreadFailed(GetLastError()); 51 | return StartThreadResult.success(handle); 52 | } 53 | else static assert("mar.thread.startThread not implemented on this platform"); 54 | } -------------------------------------------------------------------------------- /src/mar/traits.d: -------------------------------------------------------------------------------- 1 | module mar.traits; 2 | 3 | template isArithmetic(T) 4 | { 5 | // TODO: handle wrapped arithmetic types 6 | enum isArithmetic = __traits(isArithmetic, T); 7 | } 8 | /* 9 | template isIntegral(T) 10 | { 11 | enum isIntegral = 12 | is(typeof(T.init == 0)) // equatable to 0 13 | ; 14 | } 15 | */ 16 | 17 | /* 18 | template Unconst(T) 19 | { 20 | static if (is(T U == immutable U)) alias Unconst = U; 21 | else static if (is(T U == inout const U)) alias Unconst = U; 22 | else static if (is(T U == inout U)) alias Unconst = U; 23 | else static if (is(T U == const U)) alias Unconst = U; 24 | else alias Unconst = T; 25 | } 26 | */ 27 | 28 | /** 29 | Removes all qualifiers, if any, from type `T`. 30 | */ 31 | template Unqual(T) 32 | { 33 | version (none) // Error: recursive alias declaration @@@BUG1308@@@ 34 | { 35 | static if (is(T U == const U)) alias Unqual = Unqual!U; 36 | else static if (is(T U == immutable U)) alias Unqual = Unqual!U; 37 | else static if (is(T U == inout U)) alias Unqual = Unqual!U; 38 | else static if (is(T U == shared U)) alias Unqual = Unqual!U; 39 | else alias Unqual = T; 40 | } 41 | else // workaround 42 | { 43 | static if (is(T U == immutable U)) alias Unqual = U; 44 | else static if (is(T U == shared inout const U)) alias Unqual = U; 45 | else static if (is(T U == shared inout U)) alias Unqual = U; 46 | else static if (is(T U == shared const U)) alias Unqual = U; 47 | else static if (is(T U == shared U)) alias Unqual = U; 48 | else static if (is(T U == inout const U)) alias Unqual = U; 49 | else static if (is(T U == inout U)) alias Unqual = U; 50 | else static if (is(T U == const U)) alias Unqual = U; 51 | else alias Unqual = T; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/mar/typecons.d: -------------------------------------------------------------------------------- 1 | module mar.typecons; 2 | 3 | /+ 4 | ref T unconst(T)(ref const(T) value) 5 | { 6 | pragma(inline, true); 7 | return cast(T)value; 8 | } 9 | T unconst(T)(const(T) value) 10 | { 11 | pragma(inline, true); 12 | return cast(T)value; 13 | } 14 | +/ 15 | T unconst(T)(const(T) obj) 16 | { 17 | pragma(inline, true); 18 | return cast(T)obj; 19 | } 20 | 21 | template unsignedType(size_t size) 22 | { 23 | static if (size == 1) 24 | alias unsignedType = ubyte; 25 | static if (size == 2) 26 | alias unsignedType = ushort; 27 | static if (size == 4) 28 | alias unsignedType = uint; 29 | static if (size == 8) 30 | alias unsignedType = ulong; 31 | else static assert(0, "no unsignedType for this size"); 32 | } 33 | 34 | template defaultNullValue(T) 35 | { 36 | static if (is(T == enum)) 37 | { 38 | static if (T.min - 1 < T.min) enum defaultNullValue = cast(T)(T.min - 1); 39 | else static if (T.max + 1 > T.max) enum defaultNullValue = cast(T)(T.max + 1); 40 | else static assert(0, "cannot find a default null value for " ~ T.stringof); 41 | } 42 | else static assert(0, "cannot find a default null value for " ~ T.stringof); 43 | } 44 | 45 | struct Nullable(T, T nullValue = defaultNullValue!T) 46 | { 47 | private T value = nullValue; 48 | T unsafeGetValue() inout { pragma(inline, true); return cast(T)value; } 49 | T get() inout in { assert(value !is nullValue); } do { return cast(T)value; } 50 | bool isNull() const { return value is nullValue; } 51 | void opAssign(T value) 52 | { 53 | this.value = value; 54 | } 55 | } 56 | 57 | auto nullable(T)(T value) { return Nullable!T(value); } 58 | 59 | version (NoStdc) { } else 60 | { 61 | template ErrnoOrValue(T) 62 | { 63 | struct ErrnoOrValue 64 | { 65 | static ErrnoOrValue error() 66 | { 67 | return ErrnoOrValue.init; 68 | } 69 | 70 | private T _value = void; 71 | private bool _isError = true; // TODO: some types may not need an error field 72 | 73 | this(T value) 74 | { 75 | this._value = value; 76 | this._isError = false; 77 | } 78 | 79 | bool isError() const { return _isError; } 80 | auto errorCode() const 81 | { 82 | import core.stdc.errno : errno; 83 | return errno; 84 | } 85 | T value() const { return cast(T)_value; } 86 | } 87 | } 88 | } 89 | 90 | struct ValueOrErrorCode(Value, ErrorCode) 91 | { 92 | static ValueOrErrorCode!(Value, ErrorCode) error(ErrorCode errorCode) 93 | in { assert(errorCode != ErrorCode.init); } do 94 | { 95 | ValueOrErrorCode!(Value, ErrorCode) result; 96 | result._errorCode = errorCode; 97 | return result; 98 | } 99 | 100 | private Value _value = void; 101 | private ErrorCode _errorCode; 102 | 103 | this(Value value) 104 | { 105 | this._value = value; 106 | } 107 | 108 | bool failed() const { pragma(inline, true); return _errorCode != ErrorCode.init; } 109 | bool passed() const { pragma(inline, true); return _errorCode == ErrorCode.init; } 110 | ErrorCode errorCode() const { pragma(inline, true); return cast(ErrorCode)_errorCode; } 111 | 112 | void set(Value value) { pragma(inline, true); this._value = value; } 113 | Value val() const { pragma(inline, true); return cast(Value)_value; } 114 | } 115 | 116 | auto enforce(T, U...)(T value, U errorMsgArgs) 117 | { 118 | pragma(inline, true); 119 | if (value.failed) 120 | { 121 | import mar.process : exit; 122 | import mar.file : stderr; 123 | stderr.write(errorMsgArgs); 124 | exit(1); 125 | } 126 | return value.val; 127 | } -------------------------------------------------------------------------------- /src/mar/windows/coreaudio.d: -------------------------------------------------------------------------------- 1 | module audio.windows.coreaudio; 2 | 3 | import mar.c : cint, cwstring; 4 | import mar.windows : ClsCtx, PropVariant; 5 | 6 | enum DataFlow 7 | { 8 | render = 0, 9 | capture = 1, 10 | both = 2, 11 | /* 12 | render = 0x01, 13 | capture = 0x02, 14 | both = render | capture, 15 | */ 16 | } 17 | 18 | public enum DeviceState : uint 19 | { 20 | active = 0x00000001, /// Same as DEVICE_STATE_ACTIVE 21 | disabled = 0x00000002, /// Same as DEVICE_STATE_DISABLED 22 | notPresent = 0x00000004, /// Same as DEVICE_STATE_NOTPRESENT 23 | unplugged = 0x00000008, /// Same as DEVICE_STATE_UNPLUGGED 24 | all = 0x0000000F, /// Same as DEVICE_STATEMASK_ALL 25 | } 26 | public enum StorageAccessMode : uint 27 | { 28 | read, 29 | write, 30 | readWrite, 31 | } 32 | 33 | enum Role 34 | { 35 | console = 0, 36 | multimedia = 1, 37 | communications = 2, 38 | } 39 | 40 | // Equivalent to PROPERTYKEY 41 | struct PropertyKey 42 | { 43 | import mar.windows : Guid; 44 | 45 | Guid formatID; 46 | int propertyId; 47 | } 48 | 49 | struct IPropertyStore 50 | { 51 | extern (Windows) cint function(cint* propCount) getCount; 52 | extern (Windows) cint function(cint property, PropertyKey* key) getAt; 53 | extern (Windows) cint function(ref PropertyKey key, PropVariant* value) getValue; 54 | extern (Windows) cint function(ref PropertyKey key, PropVariant* value) setValue; 55 | extern (Windows) cint function() commit; 56 | } 57 | 58 | struct IMMDevice 59 | { 60 | import mar.windows : HResult; 61 | import mar.windows.ole32.nolink : InterfaceID, InterfaceMixin, IUnknown; 62 | 63 | static __gshared immutable id = InterfaceID.fromString!"D666063F-1587-4E43-81F1-B948E807363F"; 64 | 65 | mixin template VTableMixin(T) 66 | { 67 | mixin IUnknown.VTableMixin!T; 68 | extern (Windows) HResult function(T*, const(InterfaceID)* id, ClsCtx clsCtx, PropVariant* activationParams, void** outInterface) activate; 69 | extern (Windows) HResult function(T*, StorageAccessMode stgmAccess, out IPropertyStore properties) openPropertyStore; 70 | extern (Windows) HResult function(T*, cwstring* id) getId; 71 | extern (Windows) HResult function(T*, DeviceState* state) getState; 72 | } 73 | mixin InterfaceMixin!(VTableMixin, typeof(this)); 74 | } 75 | struct IMMDeviceCollection 76 | { 77 | import mar.windows : HResult; 78 | import mar.windows.ole32.nolink : InterfaceID, InterfaceMixin, IUnknown; 79 | 80 | static __gshared immutable id = InterfaceID.fromString!"0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"; 81 | 82 | mixin template VTableMixin(T) 83 | { 84 | extern (Windows) HResult function(T*, cint* numDevices) getCount; 85 | extern (Windows) HResult function(T*, cint deviceNumber, IMMDevice* device) item; 86 | } 87 | mixin InterfaceMixin!(VTableMixin, typeof(this)); 88 | } 89 | struct IMMNotificationClient 90 | { 91 | import mar.windows : HResult; 92 | import mar.windows.ole32.nolink : InterfaceID, InterfaceMixin, IUnknown; 93 | 94 | static __gshared immutable id = InterfaceID.fromString!"7991EEC9-7E89-4D85-8390-6C703CEC60C0"; 95 | 96 | mixin template VTableMixin(T) 97 | { 98 | mixin IUnknown.VTableMixin!T; 99 | extern (Windows) void function(T*, cwstring deviceId, DeviceState newState) onDeviceStateChanged; 100 | extern (Windows) void function(T*, cwstring pwstrDeviceId) onDeviceAdded; 101 | extern (Windows) void function(T*, cwstring deviceId) onDeviceRemoved; 102 | extern (Windows) void function(T*, DataFlow flow, Role role, cwstring defaultDeviceId) onDefaultDeviceChanged; 103 | extern (Windows) void function(T*, cwstring pwstrDeviceId, PropertyKey key) onPropertyValueChanged; 104 | } 105 | mixin InterfaceMixin!(VTableMixin, typeof(this)); 106 | } 107 | struct IMMDeviceEnumerator 108 | { 109 | import mar.windows : HResult; 110 | import mar.windows.ole32.nolink : InterfaceID, InterfaceMixin, IUnknown; 111 | 112 | static __gshared immutable id = InterfaceID.fromString!"A95664D2-9614-4F35-A746-DE8DB63617E6"; 113 | 114 | mixin template VTableMixin(T) 115 | { 116 | mixin IUnknown.VTableMixin!T; 117 | extern (Windows) HResult function(T* obj, DataFlow dataFlow, DeviceState stateMask, 118 | IMMDeviceCollection* devices) enumAudioEndpoints; 119 | static assert(enumAudioEndpoints.offsetof == size_t.sizeof * 3); 120 | extern (Windows) HResult function(T* obj, DataFlow dataFlow, Role role, IMMDevice** endpoint) getDefaultAudioEndpoint; 121 | static assert(getDefaultAudioEndpoint.offsetof == size_t.sizeof * 4); 122 | extern (Windows) HResult function(T* obj, cwstring id, IMMDevice* deviceName) getDevice; 123 | extern (Windows) HResult function(T* obj, IMMNotificationClient client) registerEndpointNotificationCallback; 124 | extern (Windows) HResult function(T* obj, IMMNotificationClient client) unregisterEndpointNotificationCallback; 125 | } 126 | mixin InterfaceMixin!(VTableMixin, typeof(this)); 127 | 128 | } 129 | struct MMDeviceEnumerator 130 | { 131 | import mar.windows.ole32.nolink : ClassID; 132 | 133 | static __gshared immutable id = ClassID.fromString!"BCDE0395-E52F-467C-8E3D-C4579291692E"; 134 | } 135 | 136 | enum AudioClientShareMode 137 | { 138 | shared_, /// AUDCLNT_SHAREMODE_SHARED 139 | exclusive, /// AUDCLNT_SHAREMODE_EXCLUSIVE 140 | } 141 | enum AudioClientStreamFlags : uint 142 | { 143 | none = 0, 144 | crossProcess = 0x00010000, /// AUDCLNT_STREAMFLAGS_CROSSPROCESS 145 | loopback = 0x00020000, /// AUDCLNT_STREAMFLAGS_LOOPBACK 146 | eventCallback = 0x00040000, /// AUDCLNT_STREAMFLAGS_EVENTCALLBACK 147 | noPersist = 0x00080000, /// AUDCLNT_STREAMFLAGS_NOPERSIST 148 | } 149 | 150 | struct IAudioClient 151 | { 152 | import mar.windows : HResult, Handle, Guid; 153 | import mar.windows.ole32.nolink : InterfaceID, InterfaceMixin, IUnknown; 154 | import mar.windows.winmm.nolink : WaveFormatEx; 155 | 156 | static __gshared immutable id = InterfaceID.fromString!"1CB9AD4C-DBFA-4c32-B178-C2F568A703B2"; 157 | 158 | mixin template VTableMixin(T) 159 | { 160 | mixin IUnknown.VTableMixin!T; 161 | extern (Windows) HResult function(T*, 162 | AudioClientShareMode shareMode, 163 | AudioClientStreamFlags streamFlags, 164 | long fufferDuration, // REFERENCE_TIME 165 | long periodicity, // REFERENCE_TIME 166 | const(WaveFormatEx)* format, 167 | const(Guid)* audioSessionGuid) initialize; 168 | extern (Windows) HResult function(T*, uint* bufferSize) getBufferSize; 169 | extern (Windows) HResult function(T*, long* latency) getStreamLatency; 170 | extern (Windows) HResult function(T*, uint* currentPadding) getCurrentPadding; 171 | extern (Windows) HResult function(T*, 172 | AudioClientShareMode shareMode, 173 | const(WaveFormatEx)* format, 174 | WaveFormatEx* closestMatchFormat) isFormatSupported; 175 | extern (Windows) HResult function(T*, WaveFormatEx** deviceFormatPointer) getMixFormat; 176 | extern (Windows) HResult function(T*, 177 | long* defaultDevicePeriod, 178 | long* minimumDevicePeriod) getDevicePeriod; 179 | extern (Windows) HResult function(T*) start; 180 | extern (Windows) HResult function(T*) stop; 181 | extern (Windows) HResult function(T*) reset; 182 | extern (Windows) HResult function(T*, Handle eventHandle) setEventHandle; 183 | extern (Windows) HResult function(T*, const(InterfaceID)* id, void** obj) getService; 184 | 185 | } 186 | mixin InterfaceMixin!(VTableMixin, typeof(this)); 187 | } 188 | 189 | struct IAudioRenderClient 190 | { 191 | import mar.windows : HResult; 192 | import mar.windows.ole32.nolink : InterfaceID, InterfaceMixin, IUnknown; 193 | 194 | static __gshared immutable id = InterfaceID.fromString!"F294ACFC-3146-4483-A7BF-ADDCA7C260E2"; 195 | 196 | mixin template VTableMixin(T) 197 | { 198 | mixin IUnknown.VTableMixin!T; 199 | extern (Windows) HResult function(T*, uint frameCount, void** bufferPtr) getBuffer; 200 | extern (Windows) HResult function(T*, uint frameCount, uint flags) releaseBuffer; 201 | 202 | } 203 | mixin InterfaceMixin!(VTableMixin, typeof(this)); 204 | } 205 | -------------------------------------------------------------------------------- /src/mar/windows/file.d: -------------------------------------------------------------------------------- 1 | module mar.windows.file; 2 | 3 | import mar.passfail; 4 | import mar.wrap; 5 | import mar.c : cstring; 6 | 7 | import mar.windows : Handle; 8 | import mar.windows.kernel32 : 9 | GetLastError, WriteFile; 10 | 11 | uint getLastErrorMustBeNonZero() 12 | { 13 | auto result = GetLastError(); 14 | return (result == 0) ? 500 : result; 15 | } 16 | 17 | enum FileShareMode : uint 18 | { 19 | read = 0x1, 20 | write = 0x2, 21 | delete_ = 0x4, 22 | } 23 | 24 | /* 25 | | if file exists | if file doesn't exist | success error code | 26 | ----------------|-----------------------|-----------------------|------------------------------------------| 27 | createNew | ERROR_FILE_EXISTS(80) | new file | N/A | 28 | createAlways | overwrite file | new file | ERROR_ALREADY_EXISTS(183) if file exists | 29 | openExisting | open existing |ERROR_FILE_NOT_FOUND(2)| N/A | 30 | openAlways | open existing | new file | ERROR_ALREADY_EXISTS(183) if file exists | 31 | truncateExisting| overwrite file |ERROR_FILE_NOT_FOUND(2)| N/A | 32 | */ 33 | enum FileCreateMode : uint 34 | { 35 | createNew = 1, // create a new file if it doesn't exist, otherwise, fail with ERROR_FILE_EXISTS(80) 36 | createAlways = 2, // create a new file, truncate it if it already exists and set error code to ERROR_ALREADY_EXISTS(183) 37 | openExisting = 3, // open the file if it exists, otherwise, fail with ERROR_FILE_NOT_FOUND(2) 38 | openAlways = 4, // open the file or create a new empty file if it doesn't exist 39 | truncateExisting = 5, // open the file and trucate it if it exists, otherwise, fail with ERROR_FILE_NOT_FOUND(2) 40 | } 41 | 42 | struct WriteResult 43 | { 44 | private uint _errorCode; 45 | private uint _writtenOnFailure; 46 | bool failed() const { pragma(inline, true); return _errorCode != 0; } 47 | bool passed() const { pragma(inline, true); return _errorCode == 0; } 48 | 49 | uint onFailWritten() const 50 | in { assert(_errorCode != 0, "code bug"); } do 51 | { pragma(inline, true); return _writtenOnFailure; } 52 | uint errorCode() const { pragma(inline, true); return _errorCode; } 53 | } 54 | 55 | extern (C) WriteResult tryWrite(Handle handle, const(void)* ptr, size_t n) 56 | { 57 | size_t initialSize = n; 58 | while (n > 0) 59 | { 60 | auto nextLength = cast(uint)n; 61 | uint written; 62 | auto result = WriteFile(handle, ptr, nextLength, &written, null); 63 | n -= written; 64 | if (result.failed) 65 | return WriteResult(getLastErrorMustBeNonZero(), initialSize - n); 66 | } 67 | return WriteResult(0); 68 | } 69 | 70 | struct FileD 71 | { 72 | import mar.expect; 73 | 74 | private ptrdiff_t _value = -1; 75 | this(typeof(_value) value) pure nothrow @nogc 76 | { 77 | this._value = value; 78 | } 79 | bool isValid() nothrow @nogc const { return _value != -1; } 80 | ptrdiff_t numval() const { pragma(inline, true); return _value; } 81 | void setInvalid() { this._value = -1; } 82 | static FileD invalidValue() { return FileD(-1); } 83 | mixin WrapperFor!"_value"; 84 | mixin WrapOpCast; 85 | 86 | final void close() const 87 | { 88 | pragma(inline, true); 89 | import mar.windows.kernel32 : CloseHandle; 90 | CloseHandle(Handle(_value)); 91 | } 92 | 93 | Handle asHandle() const { pragma(inline, true); return Handle(_value); } 94 | 95 | auto print(P)(P printer) const 96 | { 97 | import mar.print : printDecimal; 98 | return printDecimal(printer, _value); 99 | } 100 | 101 | WriteResult tryWrite(const(void)* ptr, size_t n) { pragma(inline, true); return .tryWrite(asHandle, ptr, n); } 102 | WriteResult tryWrite(const(void)[] array) { pragma(inline, true); return .tryWrite(asHandle, array.ptr, array.length); } 103 | 104 | void write(T...)(T args) const 105 | { 106 | import mar.print : DefaultBufferedFilePrinterPolicy, BufferedFilePrinter, printArgs; 107 | alias Printer = BufferedFilePrinter!DefaultBufferedFilePrinterPolicy; 108 | 109 | char[DefaultBufferedFilePrinterPolicy.bufferLength] buffer; 110 | auto printer = Printer(this, buffer.ptr, 0); 111 | // TODO: check return value 112 | printArgs(&printer, args); 113 | printer.flush(); 114 | } 115 | 116 | void writeln(T...)(T args) const 117 | { 118 | pragma(inline, true); 119 | write(args, '\n'); 120 | } 121 | 122 | passfail tryFlush() 123 | { 124 | pragma(inline, true); 125 | import mar.windows.kernel32 : FlushFileBuffers; 126 | 127 | if (FlushFileBuffers(asHandle).failed) 128 | return passfail.fail; 129 | return passfail.pass; 130 | } 131 | 132 | mixin ExpectMixin!("ReadResult", uint, 133 | ErrorCase!("readFileFailed", "ReadFile failed, error=%", uint)); 134 | auto read(T)(T[] buffer) const if (T.sizeof <= 1) 135 | { 136 | pragma(inline, true); 137 | import mar.windows.kernel32 : ReadFile; 138 | uint bytesRead; 139 | if (ReadFile(asHandle, buffer.ptr, 1, &bytesRead, null).failed) 140 | return ReadResult.readFileFailed(GetLastError()); 141 | return ReadResult.success(bytesRead); 142 | } 143 | } 144 | 145 | bool fileExists(cstring filename) 146 | { 147 | assert(0, "not impl');"); 148 | } 149 | 150 | bool isDir(cstring path) 151 | { 152 | import mar.windows : FileAttributes; 153 | import mar.windows.kernel32 : GetFileAttributesA; 154 | 155 | auto result = GetFileAttributesA(path); 156 | return result.isValid && ((result.val & FileAttributes.directory) != 0); 157 | } 158 | 159 | enum OpenAccess : uint 160 | { 161 | //none = 0b00, // neither read or write access 162 | readOnly = 0x80000000, // GENERIC_READ 163 | writeOnly = 0x40000000, // GENERIC_WRITE 164 | readWrite = readOnly | writeOnly, 165 | } 166 | -------------------------------------------------------------------------------- /src/mar/windows/filesys.d: -------------------------------------------------------------------------------- 1 | module mar.windows.filesys; 2 | -------------------------------------------------------------------------------- /src/mar/windows/kernel32/link.d: -------------------------------------------------------------------------------- 1 | module windows.kernel32; 2 | 3 | // Note: the reason for separating function definitions by which dll 4 | // they appear in is so they can all be in the same file as the 5 | // pragma that will cause the library to be linked 6 | pragma(lib, "kernel32.lib"); 7 | 8 | import mar.wrap; 9 | import mar.c : cint, cstring; 10 | import mar.windows : 11 | Handle, ModuleHandle, SecurityAttributes, SRWLock, 12 | ThreadEntry, ThreadPriority, InputRecord; 13 | import mar.windows.kernel32.nolink; 14 | import mar.windows.file : OpenAccess, FileShareMode, FileCreateMode, FileD; 15 | 16 | extern (Windows) uint GetLastError() nothrow @nogc; 17 | extern (Windows) Handle GetStdHandle(uint handle) nothrow @nogc; 18 | 19 | extern (Windows) void DebugBreak() @nogc; 20 | extern (Windows) void ExitProcess(uint) nothrow @nogc; 21 | extern (Windows) BoolExpectNonZero CloseHandle(Handle) nothrow @nogc; 22 | 23 | extern (Windows) Handle GetCurrentProcess(); 24 | extern (Windows) uint GetCurrentThreadId(); 25 | 26 | enum HeapCreateOptions : uint 27 | { 28 | none = 0, 29 | noSerialize = 0x00000001, 30 | generateExceptions = 0x00000004, 31 | enableExecute = 0x00040000, 32 | } 33 | extern (Windows) Handle HeapCreate( 34 | HeapCreateOptions options, 35 | size_t initialSize, 36 | size_t maxSize 37 | ) @nogc; 38 | 39 | enum HeapAllocOptions : uint 40 | { 41 | none = 0, 42 | noSerialize = 0x00000001, 43 | generateExceptions = 0x00000004, 44 | zeroMemory = 0x00000008, 45 | } 46 | extern (Windows) void* HeapAlloc( 47 | Handle heap, 48 | HeapAllocOptions options, 49 | size_t size 50 | ) @nogc; 51 | 52 | enum HeapFreeOptions : uint 53 | { 54 | none = 0, 55 | noSerialize = 0x00000001, 56 | } 57 | extern (Windows) BoolExpectNonZero HeapFree( 58 | Handle hHeap, 59 | HeapFreeOptions options, 60 | void* ptr 61 | ); 62 | 63 | extern (Windows) void Sleep(uint millis) nothrow @nogc; 64 | 65 | extern (Windows) BoolExpectNonZero WriteFile( 66 | const Handle handle, 67 | const(void)* buffer, 68 | uint length, 69 | uint* written, 70 | void* overlapped 71 | ) nothrow @nogc; 72 | extern (Windows) BoolExpectNonZero ReadFile( 73 | const Handle handle, 74 | const(void)* buffer, 75 | uint length, 76 | uint* read, 77 | void* overlapped 78 | ) nothrow @nogc; 79 | 80 | extern (Windows) FileAttributesOrError GetFileAttributesA( 81 | cstring filename 82 | ) nothrow @nogc; 83 | 84 | extern (Windows) BoolExpectNonZero CreateDirectoryA( 85 | cstring filename, 86 | void* securityAttrs, 87 | ) nothrow @nogc; 88 | 89 | extern (Windows) uint GetFileSize( 90 | Handle file, 91 | uint* fileSizeHigh, 92 | ) nothrow @nogc; 93 | extern (Windows) FileD CreateFileA( 94 | cstring filename, 95 | OpenAccess access, 96 | FileShareMode shareMode, 97 | void* securityAttrs, 98 | FileCreateMode createMode, 99 | uint flagsAndAttributes, 100 | Handle tempalteFile 101 | ) nothrow @nogc; 102 | extern (Windows) Handle CreateFileMappingA( 103 | Handle file, 104 | SecurityAttributes* attributes, 105 | uint protect, 106 | uint maxSizeHigh, 107 | uint maxSizeLow, 108 | cstring name 109 | ) nothrow @nogc; 110 | extern (Windows) void* MapViewOfFile( 111 | Handle fileMappingObject, 112 | uint desiredAccess, 113 | uint offsetHigh, 114 | uint offsetLow, 115 | size_t size 116 | ) nothrow @nogc; 117 | extern (Windows) BoolExpectNonZero UnmapViewOfFile(void* ptr); 118 | 119 | extern (Windows) void InitializeSRWLock(SRWLock* lock); 120 | extern (Windows) void AcquireSRWLockExclusive(SRWLock* lock); 121 | extern (Windows) void ReleaseSRWLockExclusive(SRWLock* lock); 122 | 123 | extern (Windows) Handle CreateEventA( 124 | SecurityAttributes* eventAttributes, 125 | cint manualReset, 126 | cint initialState, 127 | cstring name 128 | ); 129 | extern (Windows) BoolExpectNonZero SetEvent(Handle handle); 130 | extern (Windows) BoolExpectNonZero ResetEvent(Handle handle); 131 | 132 | extern (Windows) BoolExpectNonZero QueryPerformanceFrequency(long* frequency); 133 | extern (Windows) BoolExpectNonZero QueryPerformanceCounter(long* count); 134 | 135 | extern (Windows) uint WaitForSingleObject( 136 | Handle handle, 137 | uint millis 138 | ); 139 | 140 | extern (Windows) Handle CreateThread( 141 | SecurityAttributes* attributes, 142 | size_t stackSize, 143 | ThreadEntry entry, 144 | void* parameter, 145 | uint creationFlags, 146 | uint* threadID 147 | ); 148 | extern (Windows) Handle GetCurrentThread(); 149 | extern (Windows) cint GetThreadPriority(Handle thread); 150 | extern (Windows) BoolExpectNonZero SetThreadPriority( 151 | Handle thread, 152 | ThreadPriority priority 153 | ); 154 | 155 | extern (Windows) ModuleHandle LoadLibraryA(cstring fileName); 156 | extern (Windows) void* GetProcAddress(ModuleHandle, cstring fileName); 157 | 158 | extern (Windows) BoolExpectNonZero FlushFileBuffers(Handle file); 159 | 160 | extern (Windows) BoolExpectNonZero GetConsoleMode( 161 | Handle consoleHandle, uint* mode); 162 | extern (Windows) BoolExpectNonZero SetConsoleMode( 163 | Handle consoleHandle, uint mode); 164 | extern (Windows) BoolExpectNonZero ReadConsoleInputA( 165 | Handle consoleHandle, 166 | InputRecord* buffer, 167 | uint length, 168 | uint* numberOfEventsRead 169 | ); 170 | -------------------------------------------------------------------------------- /src/mar/windows/kernel32/nolink.d: -------------------------------------------------------------------------------- 1 | module mar.windows.kernel32.nolink; 2 | 3 | /** 4 | Represents semantics of the windows BOOL return type. 5 | */ 6 | struct BoolExpectNonZero 7 | { 8 | import mar.c : cint; 9 | 10 | private cint _value; 11 | bool failed() const { pragma(inline, true); return _value == 0; } 12 | bool passed() const { pragma(inline, true); return _value != 0; } 13 | 14 | auto print(P)(P printer) const 15 | { 16 | return printer.put(passed ? "TRUE" : "FALSE"); 17 | } 18 | } 19 | 20 | struct FileAttributesOrError 21 | { 22 | import mar.windows : FileAttributes; 23 | 24 | private static FileAttributes invalidEnumValue() { pragma(inline, true); return cast(FileAttributes)-1; } 25 | static FileAttributesOrError invalidValue() { pragma(inline, true); return FileAttributesOrError(invalidEnumValue); } 26 | 27 | private FileAttributes _attributes; 28 | bool isValid() const { return _attributes != invalidEnumValue; } 29 | FileAttributes val() const { return _attributes; } 30 | } 31 | -------------------------------------------------------------------------------- /src/mar/windows/kernel32/package.d: -------------------------------------------------------------------------------- 1 | module mar.windows.kernel32; 2 | 3 | public import mar.windows.kernel32.nolink; 4 | public import mar.windows.kernel32.link; 5 | -------------------------------------------------------------------------------- /src/mar/windows/mmap.d: -------------------------------------------------------------------------------- 1 | module mar.windows.mmap; 2 | 3 | import mar.flag; 4 | import mar.file : FileD; 5 | alias off_t = size_t; // todo: this goes somewhere else 6 | struct MemoryMap 7 | { 8 | void* _ptr; 9 | void* ptr() const { return cast(void*)_ptr; } 10 | ~this() { unmap(); } 11 | void unmap() 12 | { 13 | if (_ptr) 14 | { 15 | assert(0, "unmap not implemented on windows"); 16 | //munmap(_ptr, length); 17 | _ptr = null; 18 | } 19 | } 20 | } 21 | MemoryMap createMemoryMap(void* addrHint, size_t length, 22 | Flag!"writeable" writeable, FileD fd, off_t fdOffset) 23 | { 24 | assert(0, "createMemoryMap not implemented on Windows"); 25 | } -------------------------------------------------------------------------------- /src/mar/windows/mscoree/link.d: -------------------------------------------------------------------------------- 1 | /** 2 | OLE = Object Linking and Embedding 3 | */ 4 | module mar.windows.mscoree.link; 5 | 6 | version (DisablePragmaLib) { } 7 | else { 8 | pragma(lib, "mscoree.lib"); 9 | } 10 | 11 | import mar.windows : HResult; 12 | import mar.windows.ole32.nolink : ClassID, InterfaceID; 13 | import mar.windows.mscoree.nolink; 14 | 15 | 16 | extern (Windows) HResult CLRCreateInstance( 17 | const(ClassID)* classID, 18 | const(InterfaceID)* interfaceID, 19 | void** ctx 20 | ) nothrow @nogc; 21 | 22 | HResult CLRCreateInstanceOf(T)(const(ClassID)* classID, T** interfacePtr) 23 | { 24 | return CLRCreateInstance(classID, &T.id, cast(void**)interfacePtr); 25 | } -------------------------------------------------------------------------------- /src/mar/windows/mscoree/nolink.d: -------------------------------------------------------------------------------- 1 | module mar.windows.mscoree.nolink; 2 | 3 | 4 | struct CLRMetaHost 5 | { 6 | import mar.windows.ole32.nolink : ClassID; 7 | static __gshared immutable id = ClassID.fromString!"9280188d-0e8e-4867-b30c-7fa83884e8de"; 8 | } 9 | struct CLRMetaHostPolicy 10 | { 11 | import mar.windows.ole32.nolink : ClassID; 12 | //static __gshared immutable id = ClassID.fromString!"???"; 13 | } 14 | struct CLRDebugging 15 | { 16 | import mar.windows.ole32.nolink : ClassID; 17 | //static __gshared immutable id = ClassID.fromString!"???"; 18 | } 19 | 20 | struct ICLRMetaHost 21 | { 22 | import mar.sentinel : SentinelPtr; 23 | import mar.windows : HResult, Handle, Guid; 24 | import mar.windows.ole32.nolink : InterfaceID, InterfaceMixin, IUnknown, IEnumUnknown; 25 | 26 | static __gshared immutable id = InterfaceID.fromString!"D332DB9E-B9B3-4125-8207-A14884F53216"; 27 | 28 | mixin template VTableMixin(T) 29 | { 30 | mixin IUnknown.VTableMixin!T; 31 | extern (Windows) HResult function(T*, 32 | SentinelPtr!(const(wchar)) version_, 33 | const(InterfaceID)* interfaceID, 34 | void** runtime) getRuntime; // Should be an interface matching interfaceID 35 | 36 | extern (Windows) HResult function(T*, 37 | SentinelPtr!(const(wchar)) filePath, 38 | wchar* buffer, 39 | uint* bufferLength) getVersionFromFile; 40 | 41 | extern (Windows) HResult function(T*, 42 | IEnumUnknown** enumerator) enumerateInstalledRuntimes; 43 | 44 | extern (Windows) HResult function(T*, 45 | Handle processHandle, 46 | IEnumUnknown** enumerator) enumerateLoadedRuntimes; 47 | 48 | extern (Windows) HResult function(T*, 49 | void* reserved1) requestRuntimeLoadedNotification; 50 | 51 | extern (Windows) HResult function(T*, 52 | const(InterfaceID)* interfaceID, 53 | void** runtime) queryLegacyV2RuntimeBinding; // Should be an interface matching interfaceID 54 | } 55 | mixin InterfaceMixin!(VTableMixin, typeof(this)); 56 | 57 | HResult getRuntimeOf(T)(SentinelPtr!(const(wchar)) version_, T** iface) 58 | { 59 | return getRuntime(version_, &T.id, cast(void**)iface); 60 | } 61 | } 62 | 63 | public struct ICLRRuntimeInfo 64 | { 65 | import mar.sentinel : SentinelPtr; 66 | import mar.c : cint; 67 | import mar.windows : HResult, Handle, Guid; 68 | import mar.windows.ole32.nolink : ClassID, InterfaceID, InterfaceMixin, IUnknown; 69 | 70 | static __gshared immutable id = InterfaceID.fromString!"BD39D1D2-BA2F-486A-89B0-B4B0CB466891"; 71 | 72 | mixin template VTableMixin(T) 73 | { 74 | mixin IUnknown.VTableMixin!T; 75 | extern (Windows) HResult function(T*, 76 | wchar* buffer, 77 | uint* bufferSize) getVersionString; 78 | extern (Windows) HResult function(T*, 79 | wchar* buffer, 80 | uint* bufferSize) getRuntimeDirectory; 81 | extern (Windows) HResult function(T*, 82 | Handle processHandle, 83 | cint* loaded) isLoaded; 84 | extern (Windows) HResult function(T*, 85 | uint resourceID, 86 | SentinelPtr!wchar buffer, 87 | uint* bufferSize, 88 | uint localeID) loadErrorString; 89 | extern (Windows) HResult function(T*, 90 | SentinelPtr!(const(wchar)) dllName, 91 | Handle *module_) loadLibrary; 92 | extern (Windows) HResult function(T*, 93 | SentinelPtr!(const(wchar)) procName, 94 | void** proc) getProcAddress; 95 | extern (Windows) HResult function(T*, 96 | const(ClassID)* classID, 97 | const(InterfaceID)* interfaceID, 98 | void** outInterface) getInterface; 99 | } 100 | mixin InterfaceMixin!(VTableMixin, typeof(this)); 101 | 102 | HResult getInterfaceOf(T)(const(ClassID)* classID, T** iface) 103 | { 104 | return getInterface(classID, &T.id, cast(void**)iface); 105 | } 106 | } 107 | 108 | public struct CorRuntimeHost 109 | { 110 | import mar.windows.ole32.nolink : ClassID; 111 | static __gshared immutable id = ClassID.fromString!"CB2F6723-AB3A-11D2-9C40-00C04FA30A3E"; 112 | } 113 | public struct ICorRuntimeHost 114 | { 115 | import mar.sentinel : SentinelPtr; 116 | import mar.c : cint; 117 | import mar.windows : HResult, Handle, Guid; 118 | import mar.windows.ole32.nolink : ClassID, InterfaceID, InterfaceMixin, IUnknown; 119 | 120 | static __gshared immutable id = InterfaceID.fromString!"CB2F6722-AB3A-11d2-9C40-00C04FA30A3E"; 121 | 122 | mixin template VTableMixin(T) 123 | { 124 | mixin IUnknown.VTableMixin!T; 125 | extern (Windows) HResult function(T* obj) createLogicalThreadState; // DO NOT USE 126 | extern (Windows) HResult function(T* obj) deleteLogicalThreadState; // DO NOT USE 127 | extern (Windows) HResult function(T* obj, uint* cookie) switchinLogicalThreadState; // DO NOT USE 128 | extern (Windows) HResult function(T* obj, uint** cookie) switchoutLogicalThreadState; // DO NOT USE 129 | extern (Windows) HResult function(T* obj, uint* count) locksHeldByLogicalThread; // DO NOT USE 130 | extern (Windows) HResult function(T* obj, Handle file, Handle* outAddress) mapFile; 131 | extern (Windows) HResult function(T* obj, ICorConfiguration** config) getConfiguration; 132 | extern (Windows) HResult function(T* obj) start; 133 | extern (Windows) HResult function(T* obj) stop; 134 | extern (Windows) HResult function(T* obj, 135 | SentinelPtr!(const(wchar)) name, 136 | IUnknown* identityArray, 137 | void** appDomain) createDomain; 138 | extern (Windows) HResult function(T* obj, IUnknown** domain) getDefaultDomain; 139 | // TODO: finish the rest 140 | //alias HCORENUM = uint; // TODO: what is this? 141 | //extern (Windows) HResult function(T* obj, HCORENUM* enum_) enumDomains; 142 | //extern (Windows) HResult function(T* obj, HCORENUM enum_, void** appDomain) nextDomain; 143 | //extern (Windows) HResult function(T* obj, HCORENUM enum_) closeEnum; 144 | } 145 | mixin InterfaceMixin!(VTableMixin, typeof(this)); 146 | } 147 | 148 | public struct ICorConfiguration 149 | { 150 | } -------------------------------------------------------------------------------- /src/mar/windows/mscoree/package.d: -------------------------------------------------------------------------------- 1 | module mar.windows.mscoree; 2 | 3 | public import mar.windows.mscoree.nolink; 4 | public import mar.windows.mscoree.link; 5 | -------------------------------------------------------------------------------- /src/mar/windows/ole32/link.d: -------------------------------------------------------------------------------- 1 | /** 2 | OLE = Object Linking and Embedding 3 | */ 4 | module mar.windows.ole32.link; 5 | 6 | pragma(lib, "ole32.lib"); 7 | 8 | import mar.windows : HResult, ClsCtx; 9 | import mar.windows.ole32.nolink; 10 | 11 | extern (Windows) HResult CoInitialize( 12 | void* reserved 13 | ); 14 | 15 | extern (Windows) HResult CoInitializeEx( 16 | void* reserved, 17 | CoInit coInit 18 | ); 19 | 20 | extern (Windows) HResult CoCreateInstance( 21 | const(ClassID)* classID, 22 | void* unknownInterface, 23 | ClsCtx clsContext, 24 | const(InterfaceID)* interfaceID, 25 | void** ppv 26 | ); 27 | 28 | extern (Windows) void CoTaskMemFree( 29 | void* ptr 30 | ) nothrow @nogc; 31 | -------------------------------------------------------------------------------- /src/mar/windows/ole32/nolink.d: -------------------------------------------------------------------------------- 1 | module mar.windows.ole32.nolink; 2 | 3 | enum CoInit : uint 4 | { 5 | multiThreaded = 0, 6 | apartmentThreaded = 2, 7 | disableOle1Dd3 = 4, 8 | speedOverMemory = 8, 9 | } 10 | 11 | /// Equivalent to CLSID 12 | struct ClassID 13 | { 14 | import mar.windows : Guid; 15 | 16 | static ClassID fromString(string str)() 17 | { 18 | return ClassID(Guid.fromString!str); 19 | } 20 | 21 | private Guid guid; 22 | } 23 | /// Equivalent to IID 24 | struct InterfaceID 25 | { 26 | import mar.windows : Guid; 27 | 28 | static InterfaceID fromString(string str)() 29 | { 30 | return InterfaceID(Guid.fromString!str); 31 | } 32 | 33 | private Guid guid; 34 | } 35 | 36 | mixin template InterfaceMixin(alias VTableMixin, T) 37 | { 38 | static struct VTable { mixin VTableMixin!T; } 39 | private VTable* vtable; 40 | static foreach (func; __traits(allMembers, VTable)) 41 | { 42 | mixin("auto " ~ func ~ "(T...)(T args) {\n" 43 | ~ " return vtable." ~ func ~ "(&this, args);\n" 44 | ~ "}\n"); 45 | } 46 | } 47 | 48 | struct IUnknown 49 | { 50 | import mar.windows : HResult; 51 | 52 | __gshared static immutable id = 53 | InterfaceID.fromString!"00000000-0000-0000-C000-000000000046"; 54 | 55 | mixin template VTableMixin(T) 56 | { 57 | extern (Windows) HResult function(T* obj, const(InterfaceID)* interfaceID, void** object) queryInterface; 58 | extern (Windows) uint function(T* obj) addRef; 59 | extern (Windows) uint function(T* obj) release; 60 | static assert(queryInterface.offsetof == size_t.sizeof * 0); 61 | static assert(addRef.offsetof == size_t.sizeof * 1); 62 | static assert(release.offsetof == size_t.sizeof * 2); 63 | } 64 | mixin InterfaceMixin!(VTableMixin, typeof(this)); 65 | HResult queryInterfaceTo(T)(T** typedObj) 66 | { 67 | return queryInterface(&T.id, cast(void**)typedObj); 68 | } 69 | } 70 | 71 | struct IEnumUnknown 72 | { 73 | import mar.windows : HResult; 74 | 75 | __gshared static immutable id = InterfaceID.fromString!"00000100-0000-0000-C000-000000000046"; 76 | 77 | mixin template VTableMixin(T) 78 | { 79 | mixin IUnknown.VTableMixin!T; 80 | extern (Windows) HResult function(T* obj, uint count, IUnknown** arr, uint* arrLength) next; 81 | extern (Windows) HResult function(T* obj, uint count) skip; 82 | extern (Windows) HResult function(T* obj) reset; 83 | extern (Windows) HResult function(T* obj, IEnumUnknown** enumerator) clone; 84 | } 85 | mixin InterfaceMixin!(VTableMixin, typeof(this)); 86 | } 87 | -------------------------------------------------------------------------------- /src/mar/windows/ole32/package.d: -------------------------------------------------------------------------------- 1 | module mar.windows.ole32; 2 | 3 | public import mar.windows.ole32.nolink; 4 | public import mar.windows.ole32.link; 5 | -------------------------------------------------------------------------------- /src/mar/windows/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | Contains code to interface with windows that does not require linking to any external libraries. 3 | */ 4 | module mar.windows; 5 | 6 | import mar.wrap; 7 | import mar.c : cint; 8 | import mar.windows.file : FileD; 9 | 10 | enum FileAttributes : uint 11 | { 12 | directory = 0x800, 13 | } 14 | 15 | /** 16 | Equivalent to HRESULT 17 | */ 18 | struct HResult 19 | { 20 | private uint value; 21 | @property bool passed() const pure nothrow @nogc { return (value & 0x80000000) == 0; } 22 | @property bool failed() const pure nothrow @nogc { return (value & 0x80000000) != 0; } 23 | // Check if this is equivalent to S_FALSE 24 | @property bool isFalse() const pure nothrow @nogc { return value == 1; } 25 | // TODO: file/line number also? 26 | void enforce(E...)(E errorMsgValues) const 27 | { 28 | static import mar.enforce; 29 | mar.enforce.enforce(this, errorMsgValues); 30 | } 31 | auto print(P)(P printer) const 32 | { 33 | import mar.print : printArgs, formatHex; 34 | return printArgs(printer, "(0x", value.formatHex, 35 | " facility=0x", (0x7FF & (value >> 16)).formatHex, 36 | " code=0x", (0xFFFF & value).formatHex, ")"); 37 | } 38 | } 39 | 40 | /** 41 | Equivalent to windows HANDLE type. 42 | */ 43 | struct Handle 44 | { 45 | static Handle nullValue() { return Handle(0); } 46 | static Handle invalidValue() { return Handle(-1); } 47 | 48 | private ptrdiff_t _value = -1; 49 | this(typeof(_value) value) pure nothrow @nogc 50 | { 51 | this._value = value; 52 | } 53 | bool isValid() nothrow @nogc const { return _value != -1; } 54 | bool isNull() nothrow @nogc const { return _value == 0; } 55 | ptrdiff_t numval() nothrow @nogc const { pragma(inline, true); return _value; } 56 | void setInvalid() nothrow @nogc { this._value = -1; } 57 | mixin WrapperFor!"_value"; 58 | mixin WrapOpCast; 59 | FileD asFileD() nothrow @nogc inout { pragma(inline, true); return FileD(_value); } 60 | 61 | auto print(P)(P printer) const 62 | { 63 | import mar.print : printDecimal; 64 | return printDecimal(printer, _value); 65 | } 66 | } 67 | 68 | /** 69 | Equivalent to windows HWND type. 70 | */ 71 | struct WindowHandle 72 | { 73 | private Handle _handle; 74 | mixin WrapperFor!"_handle"; 75 | mixin WrapOpCast; 76 | } 77 | 78 | struct ModuleHandle 79 | { 80 | static Handle nullValue() { return Handle(0); } 81 | 82 | private size_t _value = 0; 83 | this(typeof(_value) value) pure nothrow @nogc 84 | { 85 | this._value = value; 86 | } 87 | bool isNull() nothrow @nogc const { return _value == 0; } 88 | ptrdiff_t numval() nothrow @nogc const { pragma(inline, true); return _value; } 89 | void setNull() nothrow @nogc { this._value = 0; } 90 | mixin WrapperFor!"_value"; 91 | mixin WrapOpCast; 92 | } 93 | 94 | struct ListEntry 95 | { 96 | ListEntry* next; 97 | ListEntry* prev; 98 | } 99 | private struct CriticalSectionDebug 100 | { 101 | ushort type; 102 | ushort creatorBackTraceIndex; 103 | CriticalSection* criticalSection; 104 | ListEntry processLocksList; 105 | uint entryCount; 106 | uint contentionCount; 107 | uint[2] spare; 108 | } 109 | struct CriticalSection 110 | { 111 | CriticalSectionDebug* debugInfo; 112 | int LockCount; 113 | int RecursionCount; 114 | Handle owningThread; 115 | Handle lockSemaphore; 116 | size_t spinCount; // TODO: is size_t right here? 117 | } 118 | 119 | struct SecurityAttributes 120 | { 121 | uint length; 122 | void* descriptor; 123 | cint inheritHandle; 124 | } 125 | 126 | 127 | struct SRWLock 128 | { 129 | void* ptr; 130 | } 131 | 132 | enum INFINITE = 0xffffffffL; 133 | enum INVALID_FILE_SIZE = 0xffffffff; 134 | 135 | struct Guid 136 | { 137 | uint a; 138 | ushort b; 139 | ushort c; 140 | ubyte[8] d; 141 | 142 | static Guid fromString(string str)() 143 | { 144 | return mixin(uuidToGuidMixinCode!str); 145 | } 146 | private template uuidToGuidMixinCode(string uuid) 147 | { 148 | static assert(uuid.length == 36, "uuid must be 36 characters"); 149 | enum uuidToGuidMixinCode = "Guid(" 150 | ~ "cast(uint) 0x" ~ uuid[ 0 .. 8] 151 | ~ ", cast(ushort)0x" ~ uuid[ 9 .. 13] 152 | ~ ", cast(ushort)0x" ~ uuid[14 .. 18] 153 | ~ ",[cast(ubyte)0x" ~ uuid[19 .. 21] 154 | ~ ", cast(ubyte)0x" ~ uuid[21 .. 23] 155 | ~ ", cast(ubyte)0x" ~ uuid[24 .. 26] 156 | ~ ", cast(ubyte)0x" ~ uuid[26 .. 28] 157 | ~ ", cast(ubyte)0x" ~ uuid[28 .. 30] 158 | ~ ", cast(ubyte)0x" ~ uuid[30 .. 32] 159 | ~ ", cast(ubyte)0x" ~ uuid[32 .. 34] 160 | ~ ", cast(ubyte)0x" ~ uuid[34 .. 36] 161 | ~ "])"; 162 | } 163 | bool opEquals(const ref Guid other) const 164 | { 165 | return a == other.a 166 | && b == b 167 | && c == c 168 | && d == d; 169 | } 170 | auto print(P)(P printer) const 171 | { 172 | import mar.print : printArgs, formatHex, formatPadLeft; 173 | return printArgs(printer 174 | , a.formatHex.formatPadLeft(8, '0') 175 | , "-", b.formatHex.formatPadLeft(4, '0') 176 | , "-", c.formatHex.formatPadLeft(4, '0') 177 | , "-", d[0].formatHex.formatPadLeft(2, '0'), d[1].formatHex.formatPadLeft(2, '0') 178 | , "-" 179 | , d[2].formatHex.formatPadLeft(2, '0') 180 | , d[3].formatHex.formatPadLeft(2, '0') 181 | , d[4].formatHex.formatPadLeft(2, '0') 182 | , d[5].formatHex.formatPadLeft(2, '0') 183 | , d[6].formatHex.formatPadLeft(2, '0') 184 | , d[7].formatHex.formatPadLeft(2, '0') 185 | ); 186 | } 187 | } 188 | 189 | alias ThreadEntry = extern (Windows) uint function(void* param); 190 | enum ThreadPriority : cint 191 | { 192 | normal = 0, 193 | aboveNormal = 1, 194 | belowNormal = -1, 195 | highest = 2, 196 | idle = -15, 197 | lowest = -2, 198 | timeCritical = 15, 199 | } 200 | 201 | struct FocusEventRecord 202 | { 203 | import mar.c : cint; 204 | 205 | cint setFocus; 206 | } 207 | struct KeyEventRecord 208 | { 209 | import mar.c : cint; 210 | 211 | cint down; 212 | ushort repeatCount; 213 | ushort keyCode; 214 | ushort scanCode; 215 | union 216 | { 217 | wchar unicodeChar; 218 | char asciiChar; 219 | } 220 | uint controlKeyState; 221 | } 222 | 223 | enum EventType 224 | { 225 | key = 0x0001, 226 | mouse = 0x0002, 227 | windowBufferSize = 0x0004, 228 | menu = 0x0008, 229 | focus = 0x0010, 230 | } 231 | struct InputRecord 232 | { 233 | ushort type; 234 | union 235 | { 236 | KeyEventRecord key; 237 | //MOUSE_EVENT_RECORD mouse; 238 | //WINDOW_BUFFER_SIZE_RECORD windowBufferSize; 239 | //MENU_EVENT_RECORD menu; 240 | FocusEventRecord focus; 241 | } 242 | } 243 | 244 | enum ConsoleFlag : uint 245 | { 246 | enableProcessedInput = 0x0001, 247 | enableLineInput = 0x0002, 248 | enableEchoInput = 0x0004, 249 | enableWindowInput = 0x0008, 250 | enableMouseInput = 0x0010, 251 | } 252 | 253 | ubyte LOBYTE(T)(T value) { pragma(inline, true); return cast(ubyte)value; } 254 | ubyte HIBYTE(T)(T value) { pragma(inline, true); return cast(ubyte)(cast(ushort)value >> 8); } 255 | ushort LOWORD(T)(T value) { pragma(inline, true); return cast(ushort)value; } 256 | ushort HIWORD(T)(T value) { pragma(inline, true); return cast(ushort)(cast(uint)value >> 16); } 257 | 258 | // 259 | // COM 260 | // 261 | enum ClsCtx : uint 262 | { 263 | inprocServer = 0x00000001, 264 | inprocHandler = 0x00000002, 265 | localServer = 0x00000004, 266 | inprocServer16 = 0x00000008, 267 | removeServer = 0x00000010, 268 | inprocHandler16 = 0x00000020, 269 | reserved1 = 0x00000040, 270 | reserved2 = 0x00000080, 271 | reserved3 = 0x00000100, 272 | reserved4 = 0x00000200, 273 | noCodeDownload = 0x00000400, 274 | reserved5 = 0x00000800, 275 | noCustomMarshal = 0x00001000, 276 | enableCodeDownload = 0x00002000, 277 | noFailureLog = 0x00004000, 278 | disableAaa = 0x00008000, 279 | enableAaa = 0x00010000, 280 | fromDefaultContext = 0x00020000, 281 | activate32BitServer = 0x00040000, 282 | activate64BitServer = 0x00080000, 283 | enableCloaking = 0x00100000, 284 | all = inprocServer | inprocHandler | localServer | removeServer, 285 | } 286 | 287 | enum VarType : ushort 288 | { 289 | empty = 0, 290 | null_ = 1, 291 | //cint = 2, 292 | //clong = 3, 293 | float_ = 4, 294 | double_ = 5, 295 | currency = 6, 296 | date = 7, 297 | string_ = 8, 298 | object = 9, 299 | error = 10, 300 | //bool_ = 11, 301 | variant = 12, 302 | dataObject = 13, 303 | decimal = 14, 304 | byte_ = 17, 305 | long_ = 20, 306 | userDefinedType = 36, 307 | array = 8192, 308 | } 309 | struct PropVariant 310 | { 311 | VarType type; static assert(type.offsetof == 0); 312 | short reserved1; static assert(reserved1.offsetof == 2); 313 | short reserved2; static assert(reserved2.offsetof == 4); 314 | short reserved3; static assert(reserved3.offsetof == 6); 315 | 316 | union VariantUnion 317 | { 318 | char char_; static assert(char_.offsetof == 0); 319 | ubyte ubyte_; static assert(ubyte_.offsetof == 0); 320 | short short_; static assert(short_.offsetof == 0); 321 | ushort ushort_; static assert(ushort_.offsetof == 0); 322 | int int_; static assert(int_.offsetof == 0); 323 | uint uint_; static assert(uint_.offsetof == 0); 324 | long long_; static assert(long_.offsetof == 0); 325 | float float_; static assert(float_.offsetof == 0); 326 | double double_; static assert(double_.offsetof == 0); 327 | Guid* guidPtr; static assert(guidPtr.offsetof == 0); 328 | // There are more possible types, add as needed 329 | } 330 | VariantUnion val; 331 | } 332 | -------------------------------------------------------------------------------- /src/mar/windows/stdio.d: -------------------------------------------------------------------------------- 1 | module mar.windows.io; 2 | 3 | import mar.windows.kernel32 : GetStdHandle; 4 | import mar.windows.file : FileD; 5 | 6 | private __gshared FileD stdinHandle = FileD.invalidValue; 7 | private __gshared FileD stdoutHandle = FileD.invalidValue; 8 | private __gshared FileD stderrHandle = FileD.invalidValue; 9 | /* 10 | static this() 11 | { 12 | version (DontInitializeStandardHandles) 13 | { 14 | 15 | } 16 | else 17 | { 18 | import mar.windows.user32; 19 | MessageBoxA(GetActiveWindow(), "initializing standard handles", "debug", MessageBoxType.ok); 20 | stdinHandle = GetStdHandle(cast(uint)cast(int)-10).asFileD; 21 | stdoutHandle = GetStdHandle(cast(uint)cast(int)-11).asFileD; 22 | stderrHandle = GetStdHandle(cast(uint)cast(int)-12).asFileD; 23 | MessageBoxA(GetActiveWindow(), "initialized standard handles", "debug", MessageBoxType.ok); 24 | } 25 | } 26 | */ 27 | FileD stdin() nothrow @nogc 28 | { 29 | if (!stdinHandle.isValid) 30 | stdinHandle = GetStdHandle(cast(uint)cast(int)-10).asFileD; 31 | return stdinHandle; 32 | } 33 | FileD stdout() nothrow @nogc 34 | { 35 | if (!stdoutHandle.isValid) 36 | stdoutHandle = GetStdHandle(cast(uint)cast(int)-11).asFileD; 37 | return stdoutHandle; 38 | } 39 | FileD stderr() nothrow @nogc 40 | { 41 | if (!stderrHandle.isValid) 42 | stderrHandle = GetStdHandle(cast(uint)cast(int)-12).asFileD; 43 | return stderrHandle; 44 | } 45 | -------------------------------------------------------------------------------- /src/mar/windows/user32/link.d: -------------------------------------------------------------------------------- 1 | module mar.windows.user32.link; 2 | 3 | pragma(lib, "kernel32.lib"); 4 | 5 | import mar.c : cint, cuint; 6 | import mar.windows : WindowHandle; 7 | import mar.windows.user32.nolink; 8 | 9 | extern (Windows) WindowHandle GetActiveWindow() nothrow @nogc; 10 | 11 | extern (Windows) cint MessageBoxA( 12 | WindowHandle window, 13 | const(char)* text, 14 | const(char)* caption, 15 | MessageBoxType type 16 | ) nothrow @nogc; 17 | -------------------------------------------------------------------------------- /src/mar/windows/user32/nolink.d: -------------------------------------------------------------------------------- 1 | module mar.windows.user32.nolink; 2 | 3 | enum MessageBoxType : cuint 4 | { 5 | ok = 0, 6 | abortRetryIgnore = 0x2, 7 | cancelTryContinue = 0x6, 8 | help = 0x4000, 9 | } 10 | -------------------------------------------------------------------------------- /src/mar/windows/user32/package.d: -------------------------------------------------------------------------------- 1 | module mar.windows.user32; 2 | 3 | public import mar.windows.user32.nolink; 4 | public import mar.windows.user32.link; 5 | -------------------------------------------------------------------------------- /src/mar/windows/winmm/link.d: -------------------------------------------------------------------------------- 1 | module mar.windows.winmm.link; 2 | 3 | pragma(lib, "winmm.lib"); 4 | 5 | import mar.windows.winmm.nolink; 6 | 7 | extern (Windows) MultimediaResult waveOutOpen( 8 | WaveoutHandle* waveout, 9 | void* deviceID, 10 | WaveFormatEx* format, 11 | void* callback, 12 | void* callbackInstance, 13 | MuitlmediaOpenFlags flags 14 | ); 15 | extern (Windows) MultimediaResult waveOutClose( 16 | WaveoutHandle waveout 17 | ); 18 | extern (Windows) MultimediaResult waveOutPrepareHeader( 19 | WaveoutHandle waveout, 20 | WaveHeader* header, 21 | uint headerSize 22 | ); 23 | extern (Windows) MultimediaResult waveOutUnprepareHeader( 24 | WaveoutHandle waveout, 25 | WaveHeader* header, 26 | uint headerSize 27 | ); 28 | extern (Windows) MultimediaResult waveOutWrite( 29 | WaveoutHandle waveout, 30 | WaveHeader* header, 31 | uint headerSize 32 | ); 33 | 34 | extern (Windows) MultimediaResult midiInOpen( 35 | MidiInHandle* outHandle, 36 | uint deviceID, 37 | void* callback, 38 | void* callbackArg, 39 | MuitlmediaOpenFlags flags 40 | ); 41 | extern (Windows) MultimediaResult midiInClose(MidiInHandle handle); 42 | extern (Windows) MultimediaResult midiInPrepareHeader( 43 | MidiInHandle handle, 44 | MidiHeader* inHeader, 45 | uint inHeaderSize 46 | ); 47 | extern (Windows) MultimediaResult midiInUnprepareHeader( 48 | MidiInHandle handle, 49 | MidiHeader* inHeader, 50 | uint inHeaderSize 51 | ); 52 | extern (Windows) MultimediaResult midiInStart(MidiInHandle handle); 53 | extern (Windows) MultimediaResult midiInStop(MidiInHandle handle); 54 | extern (Windows) MultimediaResult midiInAddBuffer( 55 | MidiInHandle handle, 56 | MidiHeader* inHeader, 57 | uint inHeaderSize 58 | ); 59 | -------------------------------------------------------------------------------- /src/mar/windows/winmm/mmreg.d: -------------------------------------------------------------------------------- 1 | module mar.windows.winmm.mmreg; 2 | 3 | /// Equivalent to WAVE_FORMAT_* 4 | enum WaveFormatTag : ushort 5 | { 6 | unknown = 0, 7 | pcm = 1, 8 | float_ = 3, 9 | extensible = 0xfffe, 10 | } 11 | 12 | /// Equivalent to WAVEFORMATEX 13 | struct WaveFormatEx 14 | { 15 | align(2): 16 | WaveFormatTag tag; 17 | ushort channelCount; 18 | uint samplesPerSec; 19 | uint avgBytesPerSec; 20 | ushort blockAlign; 21 | ushort bitsPerSample; 22 | ushort extraSize; 23 | } 24 | static assert(WaveFormatEx.sizeof == 18); 25 | 26 | /// Equivalent to SPEAKER_* 27 | enum SpeakerFlags : uint 28 | { 29 | frontLeft = 0x0001, 30 | frontRight = 0x0002, 31 | frontCenter = 0x0004, 32 | } 33 | 34 | /// Equivalent to WAVEFORMATEXTENSIBLE 35 | struct WaveFormatExtensible 36 | { 37 | align(2): 38 | import mar.windows : Guid; 39 | 40 | WaveFormatEx format; 41 | union 42 | { 43 | ushort validBitsPerSample; 44 | ushort samplesPerBlock; 45 | } 46 | SpeakerFlags channelMask; 47 | Guid subFormat; 48 | } 49 | 50 | /// Equivalent to KSDATAFORMAT_SUBTYPE_* 51 | struct KSDataFormat 52 | { 53 | import mar.windows : Guid; 54 | static __gshared immutable ieeeFloat = Guid.fromString!"00000003-0000-0010-8000-00aa00389b71"; 55 | } 56 | -------------------------------------------------------------------------------- /src/mar/windows/winmm/mmsystem.d: -------------------------------------------------------------------------------- 1 | module mar.windows.winmm.mmsystem; 2 | 3 | /// Equivalent to WAVEHDR 4 | struct WaveHeader 5 | { 6 | void* data; 7 | uint bufferLength; 8 | uint bytesRecorded; 9 | uint* user; 10 | uint flags; 11 | uint loops; 12 | WaveHeader* next; 13 | uint* reserved; 14 | } 15 | 16 | /// Equivalent to WOM_* 17 | enum WaveOutputMessage 18 | { 19 | open = 0x3bb, 20 | close = 0x3bc, 21 | done = 0x3bd, 22 | } 23 | 24 | /// Equivalent to WIM_* 25 | enum WaveInputMessage 26 | { 27 | open = 0x3be, 28 | close = 0x3bf, 29 | done = 0x3c0, 30 | } 31 | 32 | /// Equivalent to HWAVEOUT 33 | struct WaveoutHandle 34 | { 35 | import mar.wrap; 36 | import mar.windows : Handle; 37 | 38 | private Handle _handle; 39 | mixin WrapperFor!"_handle"; 40 | mixin WrapOpCast; 41 | } 42 | 43 | /// Equivalent to MMRESULT 44 | struct MultimediaResult 45 | { 46 | enum Value : uint 47 | { 48 | noError = 0, 49 | error = 1, 50 | badDeviceID = 2, 51 | notEnabled = 3, 52 | allocated = 4, 53 | invaliedHandle = 5, 54 | noDriver = 6, 55 | nbMem = 7, 56 | notSupported = 8, 57 | badErrNum = 9, 58 | invalidFlag = 10, 59 | invalidParam = 11, 60 | handleBusy = 12, 61 | invalidAlias = 13, 62 | badDb = 14, 63 | keyNotFound = 15, 64 | readError = 16, 65 | writeError = 17, 66 | deleteError = 18, 67 | valNotFound = 19, 68 | noDriverCB = 20, 69 | waveBadFormat = 32, 70 | waveStillPlaying = 33, 71 | waveUnprepared = 34 72 | } 73 | static auto opDispatch(string member)() 74 | { 75 | return MultimediaResult(mixin("Value." ~ member)); 76 | } 77 | 78 | private Value val; 79 | bool failed() const { pragma(inline, true); return val != Value.noError; } 80 | bool passed() const { pragma(inline, true); return val == Value.noError; } 81 | auto print(P)(P printer) const 82 | { 83 | pragma(inline, true); 84 | import mar.print : printArg; 85 | return printArg(printer, val); 86 | } 87 | } 88 | 89 | /// Equivalent to CALLBACK_* and WAVE_* 90 | enum MuitlmediaOpenFlags : uint 91 | { 92 | callbackFunction = 0x30000, 93 | } 94 | 95 | enum WAVE_MAPPER = cast(void*)cast(ptrdiff_t)-1; 96 | 97 | enum : uint 98 | { 99 | MIM_OPEN = 961, 100 | MIM_CLOSE = 962, 101 | MIM_DATA = 963, 102 | MIM_LONGDATA = 964, 103 | MIM_ERROR = 965, 104 | MIM_LONGERROR = 966, 105 | MIM_MOREDATA = 972, 106 | 107 | MOM_OPEN = 967, 108 | MOM_CLOSE = 968, 109 | MOM_DONE = 969, 110 | } 111 | 112 | /// Equivalent to HMIDIIN 113 | struct MidiInHandle 114 | { 115 | import mar.wrap; 116 | import mar.windows : Handle; 117 | 118 | private Handle _handle; 119 | mixin WrapperFor!"_handle"; 120 | mixin WrapOpCast; 121 | } 122 | 123 | /// Equivalent to MIDIHDR 124 | struct MidiHeader 125 | { 126 | ubyte* data; 127 | uint size; 128 | uint recorded; 129 | uint* user; 130 | uint flags; 131 | private MidiHeader* next; 132 | private uint* reserved1; 133 | uint offset; 134 | private uint*[4] reserved2; 135 | } 136 | -------------------------------------------------------------------------------- /src/mar/windows/winmm/nolink.d: -------------------------------------------------------------------------------- 1 | module mar.windows.winmm.nolink; 2 | 3 | public import mar.windows.winmm.mmsystem; 4 | public import mar.windows.winmm.mmreg; 5 | -------------------------------------------------------------------------------- /src/mar/windows/winmm/package.d: -------------------------------------------------------------------------------- 1 | module mar.windows.winmm; 2 | 3 | public import mar.windows.winmm.nolink; 4 | public import mar.windows.winmm.link; 5 | -------------------------------------------------------------------------------- /src/mar/wrap.d: -------------------------------------------------------------------------------- 1 | /** 2 | Contains code to create type wrappers. 3 | */ 4 | module mar.wrap; 5 | 6 | import mar.flag; 7 | 8 | mixin template WrapperFor(string fieldName) 9 | { 10 | alias WrapType = typeof(__traits(getMember, typeof(this), fieldName)); 11 | static assert(typeof(this).sizeof == WrapType.sizeof, 12 | "WrapperType '" ~ typeof(this).stringof ~ "' must be the same size as it's WrapType '" ~ 13 | WrapType.stringof ~ "'"); 14 | 15 | private ref auto wrappedValueRef() inout 16 | { 17 | pragma(inline, true); 18 | return __traits(getMember, typeof(this), fieldName); 19 | } 20 | } 21 | 22 | mixin template WrapOpCast() 23 | { 24 | auto opCast(T)() inout 25 | if (is(typeof(cast(T)wrappedValueRef))) 26 | { 27 | pragma(inline, true); 28 | return cast(T)wrappedValueRef; 29 | } 30 | } 31 | 32 | mixin template WrapOpUnary() 33 | { 34 | auto opUnary(string op)() inout if (op == "-") 35 | { 36 | pragma(inline, true); 37 | return inout typeof(this)(mixin(op ~ "wrappedValueRef")); 38 | } 39 | void opUnary(string op)() if (op == "++" || op == "--") 40 | { 41 | pragma(inline, true); 42 | mixin("wrappedValueRef" ~ op ~ ";"); 43 | } 44 | } 45 | 46 | mixin template WrapOpIndex() 47 | { 48 | auto ref opIndex(T)(T index) inout 49 | { 50 | pragma(inline, true); 51 | return wrappedValueRef[index]; 52 | } 53 | auto opIndexAssign(T, U)(T value, U index) 54 | { 55 | pragma(inline, true); 56 | wrappedValueRef[index] = value; 57 | } 58 | } 59 | 60 | mixin template WrapOpSlice() 61 | { 62 | auto opSlice(T, U)(T first, U second) inout 63 | { 64 | pragma(inline, true); 65 | return wrappedValueRef[first .. second]; 66 | } 67 | } 68 | 69 | mixin template WrapOpBinary(Flag!"includeWrappedType" includeWrappedType) 70 | { 71 | auto opBinary(string op)(const typeof(this) rhs) inout 72 | { 73 | pragma(inline, true); 74 | return inout typeof(this)(mixin("wrappedValueRef " ~ op ~ " rhs.wrappedValueRef")); 75 | } 76 | static if (includeWrappedType) 77 | { 78 | auto opBinary(string op)(const WrapType rhs) inout 79 | { 80 | pragma(inline, true); 81 | return inout typeof(this)(mixin("wrappedValueRef " ~ op ~ " rhs")); 82 | } 83 | } 84 | } 85 | 86 | mixin template WrapOpEquals(Flag!"includeWrappedType" includeWrappedType) 87 | { 88 | bool opEquals(const typeof(this) rhs) const 89 | { 90 | pragma(inline, true); 91 | return wrappedValueRef == rhs.wrappedValueRef; 92 | } 93 | static if (includeWrappedType) 94 | { 95 | bool opCmp(const WrapType rhs) const 96 | { 97 | pragma(inline, true); 98 | return wrappedValueRef == rhs; 99 | } 100 | } 101 | } 102 | 103 | mixin template WrapOpCmp(Flag!"includeWrappedType" includeWrappedType) 104 | { 105 | int opCmp(const typeof(this) rhs) const 106 | { 107 | pragma(inline, true); 108 | return wrappedValueRef.opCmp(rhs.wrappedValueRef); 109 | } 110 | static if (includeWrappedType) 111 | { 112 | int opCmp(const typeof(` ~ field ~ `) rhs) const 113 | { 114 | pragma(inline, true); 115 | return wrappedValueRef.opCmp(rhs); 116 | } 117 | } 118 | } 119 | mixin template WrapOpCmpIntegral(Flag!"includeWrappedType" includeWrappedType) 120 | { 121 | int opCmp(const typeof(this) rhs) const 122 | { 123 | pragma(inline, true); 124 | const result = wrappedValueRef - rhs.wrappedValueRef; 125 | if (result < 0) return -1; 126 | if (result > 0) return 1; 127 | return 0; 128 | } 129 | static if (includeWrappedType) 130 | { 131 | int opCmp(const typeof(` ~ field ~ `) rhs) const 132 | { 133 | pragma(inline, true); 134 | const result = wrappedValueRef - rhs; 135 | if (result < 0) return -1; 136 | if (result > 0) return 1; 137 | return 0; 138 | } 139 | } 140 | } 141 | --------------------------------------------------------------------------------