From 6d924a9fd7cafe43aa50f38c0cd04c44187d4993 Mon Sep 17 00:00:00 2001 From: The XOmB Overlord Date: Sat, 21 Feb 2009 22:23:22 -0500 Subject: [PATCH] Version 1.0.0 of the XOmB Bare Bones distribution! With Bare Bones, all you get is booting up, printing some stuff out, and then looping into infinity (and beyond). Have fun! --- LICENSE | 14 + README | 129 +++ build/.gitignore | 4 + build/bochsrc | 5 + build/dsss.conf | 64 ++ build/gdc-xomb | 35 + build/iso/boot/grub/menu.lst | 129 +++ build/iso/boot/grub/stage2_eltorito | Bin 0 -> 110132 bytes build/linker.ld | 125 +++ kernel/arch/select.d | 16 + kernel/arch/x86_64/boot/boot.S | 275 ++++++ kernel/arch/x86_64/boot/boot.h | 21 + kernel/arch/x86_64/boot/load.S | 94 ++ kernel/arch/x86_64/boot/multiboot.h | 104 ++ kernel/config.d | 27 + kernel/core/kmain.d | 41 + kernel/core/kprintf.d | 428 +++++++++ kernel/core/util.d | 383 ++++++++ kernel/dev/console.d | 191 ++++ kernel/runtime/dstubs.d | 1014 ++++++++++++++++++++ kernel/runtime/gcc/builtins.d | 41 + kernel/runtime/invariant.d | 28 + kernel/runtime/object.d | 1053 +++++++++++++++++++++ kernel/runtime/std/c/stdarg.d | 34 + kernel/runtime/std/intrinsic.d | 264 ++++++ kernel/runtime/std/stdarg.d | 43 + kernel/runtime/std/typeinfo/ti_AC.d | 96 ++ kernel/runtime/std/typeinfo/ti_Acdouble.d | 107 +++ kernel/runtime/std/typeinfo/ti_Acfloat.d | 105 ++ kernel/runtime/std/typeinfo/ti_Acreal.d | 108 +++ kernel/runtime/std/typeinfo/ti_Adouble.d | 116 +++ kernel/runtime/std/typeinfo/ti_Afloat.d | 115 +++ kernel/runtime/std/typeinfo/ti_Ag.d | 204 ++++ kernel/runtime/std/typeinfo/ti_Aint.d | 119 +++ kernel/runtime/std/typeinfo/ti_Along.d | 111 +++ kernel/runtime/std/typeinfo/ti_Areal.d | 117 +++ kernel/runtime/std/typeinfo/ti_Ashort.d | 134 +++ kernel/runtime/std/typeinfo/ti_C.d | 76 ++ kernel/runtime/std/typeinfo/ti_byte.d | 39 + kernel/runtime/std/typeinfo/ti_cdouble.d | 67 ++ kernel/runtime/std/typeinfo/ti_cfloat.d | 66 ++ kernel/runtime/std/typeinfo/ti_char.d | 43 + kernel/runtime/std/typeinfo/ti_creal.d | 68 ++ kernel/runtime/std/typeinfo/ti_dchar.d | 45 + kernel/runtime/std/typeinfo/ti_delegate.d | 40 + kernel/runtime/std/typeinfo/ti_double.d | 68 ++ kernel/runtime/std/typeinfo/ti_float.d | 68 ++ kernel/runtime/std/typeinfo/ti_idouble.d | 12 + kernel/runtime/std/typeinfo/ti_ifloat.d | 12 + kernel/runtime/std/typeinfo/ti_int.d | 43 + kernel/runtime/std/typeinfo/ti_ireal.d | 12 + kernel/runtime/std/typeinfo/ti_long.d | 43 + kernel/runtime/std/typeinfo/ti_ptr.d | 47 + kernel/runtime/std/typeinfo/ti_real.d | 68 ++ kernel/runtime/std/typeinfo/ti_short.d | 39 + kernel/runtime/std/typeinfo/ti_ubyte.d | 43 + kernel/runtime/std/typeinfo/ti_uint.d | 43 + kernel/runtime/std/typeinfo/ti_ulong.d | 43 + kernel/runtime/std/typeinfo/ti_ushort.d | 39 + kernel/runtime/std/typeinfo/ti_void.d | 44 + kernel/runtime/std/typeinfo/ti_wchar.d | 44 + kernel/runtime/util.d | 163 ++++ 62 files changed, 7169 insertions(+) create mode 100644 LICENSE create mode 100644 build/.gitignore create mode 100644 build/bochsrc create mode 100644 build/dsss.conf create mode 100644 build/gdc-xomb create mode 100644 build/iso/boot/grub/menu.lst create mode 100644 build/iso/boot/grub/stage2_eltorito create mode 100644 build/linker.ld create mode 100644 kernel/arch/select.d create mode 100644 kernel/arch/x86_64/boot/boot.S create mode 100644 kernel/arch/x86_64/boot/boot.h create mode 100644 kernel/arch/x86_64/boot/load.S create mode 100644 kernel/arch/x86_64/boot/multiboot.h create mode 100644 kernel/config.d create mode 100644 kernel/core/kmain.d create mode 100644 kernel/core/kprintf.d create mode 100644 kernel/core/util.d create mode 100644 kernel/dev/console.d create mode 100644 kernel/runtime/dstubs.d create mode 100644 kernel/runtime/gcc/builtins.d create mode 100644 kernel/runtime/invariant.d create mode 100644 kernel/runtime/object.d create mode 100644 kernel/runtime/std/c/stdarg.d create mode 100644 kernel/runtime/std/intrinsic.d create mode 100644 kernel/runtime/std/stdarg.d create mode 100644 kernel/runtime/std/typeinfo/ti_AC.d create mode 100644 kernel/runtime/std/typeinfo/ti_Acdouble.d create mode 100644 kernel/runtime/std/typeinfo/ti_Acfloat.d create mode 100644 kernel/runtime/std/typeinfo/ti_Acreal.d create mode 100644 kernel/runtime/std/typeinfo/ti_Adouble.d create mode 100644 kernel/runtime/std/typeinfo/ti_Afloat.d create mode 100644 kernel/runtime/std/typeinfo/ti_Ag.d create mode 100644 kernel/runtime/std/typeinfo/ti_Aint.d create mode 100644 kernel/runtime/std/typeinfo/ti_Along.d create mode 100644 kernel/runtime/std/typeinfo/ti_Areal.d create mode 100644 kernel/runtime/std/typeinfo/ti_Ashort.d create mode 100644 kernel/runtime/std/typeinfo/ti_C.d create mode 100644 kernel/runtime/std/typeinfo/ti_byte.d create mode 100644 kernel/runtime/std/typeinfo/ti_cdouble.d create mode 100644 kernel/runtime/std/typeinfo/ti_cfloat.d create mode 100644 kernel/runtime/std/typeinfo/ti_char.d create mode 100644 kernel/runtime/std/typeinfo/ti_creal.d create mode 100644 kernel/runtime/std/typeinfo/ti_dchar.d create mode 100644 kernel/runtime/std/typeinfo/ti_delegate.d create mode 100644 kernel/runtime/std/typeinfo/ti_double.d create mode 100644 kernel/runtime/std/typeinfo/ti_float.d create mode 100644 kernel/runtime/std/typeinfo/ti_idouble.d create mode 100644 kernel/runtime/std/typeinfo/ti_ifloat.d create mode 100644 kernel/runtime/std/typeinfo/ti_int.d create mode 100644 kernel/runtime/std/typeinfo/ti_ireal.d create mode 100644 kernel/runtime/std/typeinfo/ti_long.d create mode 100644 kernel/runtime/std/typeinfo/ti_ptr.d create mode 100644 kernel/runtime/std/typeinfo/ti_real.d create mode 100644 kernel/runtime/std/typeinfo/ti_short.d create mode 100644 kernel/runtime/std/typeinfo/ti_ubyte.d create mode 100644 kernel/runtime/std/typeinfo/ti_uint.d create mode 100644 kernel/runtime/std/typeinfo/ti_ulong.d create mode 100644 kernel/runtime/std/typeinfo/ti_ushort.d create mode 100644 kernel/runtime/std/typeinfo/ti_void.d create mode 100644 kernel/runtime/std/typeinfo/ti_wchar.d create mode 100644 kernel/runtime/util.d diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8a44724 --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar +14 rue de Plaisance, 75014 Paris, France + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. You just DO WHAT THE FUCK YOU WANT TO. + diff --git a/README b/README index e69de29..33a6d46 100644 --- a/README +++ b/README @@ -0,0 +1,129 @@ +This is the XOmB-barebones distribution. It comes with the bare minimum needed to get a booting OS in D. + +Except we added kprintfln. You're welcome. + +The updated version of this file is kept on our wiki, located here: +http://wiki.xomb.org/index.php?title=XOmB_Bare_Bones + +Don't expect this to be kept as up to date. We'll try, but the newest version will always be on the wiki, so check there. + +The XOmB Bare Bones distribution is a minimal 64 bit OS written in D, distilled from the main project by [[User:Wilkie|wilkie]] and [[User:Steveklabnik|Steve Klabnik]]. + + +== About == +The [http://wiki.osdev.org osdev wiki] has a page called [http://wiki.osdev.org/Bare_Bones Bare Bones]. On it, they show how to make a minimal OS written in the C programming language. When we started [http://forum.osdev.org/viewtopic.php?f=15&t=18914 discussing programming an OS in D], some people indicated an interest in having a similar example, but in the D language. + +We decided to oblige. We've distilled a minimal OS out of XOmB, and are releasing it as the "XOmB Bare Bones" distribution. + +== Get XBB == +The git repository is located [http://github.com/xomboverlord/xomb-bare-bones/tree/master here]. If you haven't used git before, check out our [[Using Git]] tutorial. + +The short lowdown: + +git clone git://github.com/xomboverlord/xomb-bare-bones.git + + +== Using XBB == + +First, a small word about compilers: GDC is dead. Unfortunatly, LDC isn't quite up to snuff yet, and while it's being worked on, unfortunatly, we have to stick with GDC. Expect things to get better in this area soon. + +The first thing that you need to do in order to use XBB is create a cross compiler. Unfortunatly, our wiki is sparse on this subject at the moment, and so is the [http://wiki.osdev.org/GDC_Cross-Compiler osdev wiki]... we're sorry. That's coming soon. + +After you have that up and running, you need to add a profile. We use dsss/rebuild to build stuff, and when we did it, it installed to /opt/etc/. So copy the gdc-xomb file from build/ (in the xomb-bare-bones directory) to /opt/etc/rebuild/ and you should be golden. Hopefully. + +To build, just run +
dsss build
from within the build/ directory. This will make 'xomb.iso'. Then feel free to run it with bochs, xen, or whatever. + +And that's it! Pretty easy, right? + +== XBB Features == +Considering that XBB is, well, bare-bones, it doesn't do much. kmain consists of one kprintfln! statement, which just prints a line to the screen. We've included a kprintfln! implementation to show off the power of D's templating system, but feel free to replace it, of course. + +== XBB Organization == + +The XBB directory looks like this: +$ tree -L 1 +. +|-- LICENSE +|-- README +|-- build +|-- kernel + +2 directories, 2 files + +We've moved all of the source into kernel, because you (at some point) will want to write userspace code. You'd probably want to make another directory in the main tree with all of that code in it. The main XOmB project has libos/ and tools/ and such. + +=== LICENSE === +XBB is distributed under the [http://sam.zoy.org/wtfpl/ WTFPL]. This is the most free license we could possibly imagine that still contains a swear word, so have at it. + +=== README === +The readme contains a copy of this page, though it's not updated quite as often as this page is. + +=== build/ === +$ tree -L 1 build/ +build/ +|-- bochsrc +|-- dsss.conf +|-- dsss.last +|-- dsss_objs +|-- iso +|-- linker.ld +|-- xomb.dump +`-- xomb.iso + +2 directories, 6 files +==== bochsrc ==== +Assuming you're running bochs, we've given you a bochsrc. + +==== dsss.conf ==== +This file tells dsss how to build XBB. + +==== dsss.last ==== +This is another file that dsss generates. + +==== dsss_objs ==== +This directory contains all of the intermediate stuff that dsss needs to do its job. + +==== iso ==== +This directory contains a copy of the actual FS contained in xomb.iso. + +==== linker.ld ==== +This script lets ld know how it should link everything together. + +==== xomb.dump ==== +A dump of the iso. Very useful for seeing exactly what asm gets generated after compiling things. + +==== xomb.iso ==== +This is the actual .iso file that needs to be burnt to a CD (or run in some other manner) to run the OS! + +=== kernel/ === +$ tree -L 1 kernel/ +kernel/ +|-- arch +|-- config.d +|-- core +|-- dev +`-- runtime + +4 directories, 1 file + +==== arch ==== +This directory contains everything pertaining to architecture. If you wanted to port XBB to arm or something, the code would go in here. + +==== config.d ==== +This sets a bunch of configuration options, mostly relating to debugging information. + +==== core ==== +This is where it all goes down. The core kernel code is located inside. + +==== dev ==== +Everything in this directory pertains to devices. + +==== runtime ==== +The kernel D runtime. This runtime is nowhere near robust as a full userspace runtime would be, as the kernel donesn't need such niceties as garbage collection. Just the bare minimum, thanks. + +== For More Info == +Check out the individual source files for information on how specific things work. We've made an attempt to heavily comment everything, so that you can see in the code itself how things work. + +If you find that you need more help, please [[Contact |contact]] us, and we'll do our best. Please let us know if there's anything that we can do to make this project better! + diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..5cdc0e0 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,4 @@ +dsss_objs/* +dsss.last +xomb.dump +xomb.iso diff --git a/build/bochsrc b/build/bochsrc new file mode 100644 index 0000000..bca0759 --- /dev/null +++ b/build/bochsrc @@ -0,0 +1,5 @@ +romimage: file=/usr/src/bochs-2.3.7/bios/BIOS-bochs-latest +megs: 128 +ata1-slave: type=cdrom, path="./xomb.iso", status=inserted +boot: cdrom +cpu: count=2, reset_on_triple_fault=0 diff --git a/build/dsss.conf b/build/dsss.conf new file mode 100644 index 0000000..dcb888c --- /dev/null +++ b/build/dsss.conf @@ -0,0 +1,64 @@ +name = xomb + +CC = x86_64-pc-elf-gcc +CFLAGS = -nostdlib -nodefaultlibs -g -DUSE_ASSERT -mcmodel=kernel + +[*] +buildflags=-dc=gdc-xomb + +[../kernel/core/kmain.d] + +buildflags=-dc=gdc-xomb -I.. + +# compile the assembly for the target + +prebuild= \ +\ +\ +echo ; \ +echo Compiling Assembly for target: x86_64 ;\ +echo '--> boot.S';\ +x86_64-pc-elf-gcc -nostdlib -nodefaultlibs -g -DUSE_ASSERT -mcmodel=kernel -c ../kernel/arch/x86_64/boot/boot.S -o dsss_objs/G/kernel.arch.x86_64.boot.boot.o ; \ +echo '--> load.S';\ +x86_64-pc-elf-gcc -nostdlib -nodefaultlibs -g -DUSE_ASSERT -mcmodel=kernel -c ../kernel/arch/x86_64/boot/load.S -o dsss_objs/G/kernel.arch.x86_64.boot.load.o ; \ +#echo '--> trampoline.S';\ +#x86_64-pc-elf-gcc -nostdlib -nodefaultlibs -g -DUSE_ASSERT -mcmodel=kernel -c ../kernel/arch/x86_64/boot/trampoline.S -o dsss_objs/G/kernel.arch.x86_64.boot.trampoline.o ;\ +\ +\ +echo ; \ +echo Compiling Kernel Runtime ; \ +echo '--> kernel/runtime/object.d';\ +x86_64-pc-elf-gdc -nostdlib -nodefaultlibs -g -DUSE_ASSERT -mcmodel=kernel -I .. -I ../kernel/runtime/. -c ../kernel/runtime/object.d -o dsss_objs/G/kernel.runtime.object.o ;\ +echo '--> kernel/runtime/invariant.d';\ +x86_64-pc-elf-gdc -nostdlib -nodefaultlibs -g -DUSE_ASSERT -mcmodel=kernel -I .. -I ../kernel/runtime/. -c ../kernel/runtime/invariant.d -o dsss_objs/G/kernel.runtime.invariant.o ;\ +echo '--> kernel/runtime/std/typeinfo/*';\ +x86_64-pc-elf-gdmd -q,-nostdlib,-nodefaultlibs,-g,-DUSE_ASSERT,-mcmodel=kernel -I.. -I../kernel/runtime/. -c `ls ../kernel/runtime/std/typeinfo/*.d` -oddsss_objs/G/. ;\ +echo '--> kernel/runtime/dstubs.d';\ +x86_64-pc-elf-gdc -nostdlib -nodefaultlibs -g -DUSE_ASSERT -mcmodel=kernel -I .. -I ../kernel/runtime/. -c ../kernel/runtime/dstubs.d -o dsss_objs/G/kernel.runtime.dstubs.o ;\ +echo '--> kernel/runtime/util.d';\ +x86_64-pc-elf-gdc -nostdlib -nodefaultlibs -g -DUSE_ASSERT -mcmodel=kernel -I .. -I ../kernel/runtime/. -c ../kernel/runtime/util.d -o dsss_objs/G/kernel.runtime.util.o ;\ +\ +echo ; \ +echo Compiling Kernel Proper ; + +# what the target is + +target = xomb.iso + +# we will need some post build foo to link and create the iso + +postbuild = \ +\ +echo ; \ +echo Creating Kernel Executable; \ +echo '--> xomb';\ +x86_64-pc-elf-ld -nostdlib -nodefaultlibs -b elf64-x86-64 -T linker.ld -o iso/boot/xomb `ls dsss_objs/G/*.o`;\ +\ +echo ;\ +echo Creating Kernel Dump; \ +echo '--> xomb.dump';\ +rm -f xomb.dump && x86_64-pc-elf-objdump -d -S -r iso/boot/xomb > xomb.dump;\ +\ +echo ;\ +echo Compiling ISO; \ +mkisofs -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 16 -boot-info-table -o xomb.iso ./iso diff --git a/build/gdc-xomb b/build/gdc-xomb new file mode 100644 index 0000000..9dabbc9 --- /dev/null +++ b/build/gdc-xomb @@ -0,0 +1,35 @@ +profile=phobos + +compiler=x86_64-pc-elf-gdc + +exeext= +objext=o + + +noversion=DigitalMars +version=GNU +testversion=linux +testversion=darwin +version=Unix +version=Posix +noversion=Windows +noversion=Win32 +noversion=Win64 +testversion=X86 +testversion=PPC +testversion=X86_64 +testversion=D_InlineAsm +testversion=D_InlineAsm_X86 +testversion=D_InlineAsm_PPC +testversion=D_InlineAsm_X86_64 +testversion=LittleEndian +testversion=BigEndian + +[compile] +cmd=x86_64-pc-elf-gdmd -q,-nostdlib,-nodefaultlibs,-g,-DUSE_ASSERT,-mcmodel=kernel -I../kernel/runtime/ -c $i + +[link] +cmd=# + +[postlink] +cmd=echo "lol fuckers" diff --git a/build/iso/boot/grub/menu.lst b/build/iso/boot/grub/menu.lst new file mode 100644 index 0000000..89a0671 --- /dev/null +++ b/build/iso/boot/grub/menu.lst @@ -0,0 +1,129 @@ +# menu.lst - See: grub(8), info grub, update-grub(8) +# grub-install(8), grub-floppy(8), +# grub-md5-crypt, /usr/share/doc/grub +# and /usr/share/doc/grub-doc/. + +## default num +# Set the default entry to the entry number NUM. Numbering starts from 0, and +# the entry number 0 is the default if the command is not used. +# +# You can specify 'saved' instead of a number. In this case, the default entry +# is the entry saved with the command 'savedefault'. +# WARNING: If you are using dmraid do not change this entry to 'saved' or your +# array will desync and will not let you boot your system. +default 0 + +## timeout sec +# Set a timeout, in SEC seconds, before automatically booting the default entry +# (normally the first entry defined). +timeout 0 + +## hiddenmenu +# Hides the menu by default (press ESC to see the menu) +hiddenmenu + +# Pretty colours +#color cyan/blue white/blue + +## password ['--md5'] passwd +# If used in the first section of a menu file, disable all interactive editing +# control (menu entry editor and command-line) and entries protected by the +# command 'lock' +# e.g. password topsecret +# password --md5 $1$gLhU0/$aW78kHK1QfV3P2b2znUoe/ +# password topsecret + +# +# examples +# +# title Windows 95/98/NT/2000 +# root (hd0,0) +# makeactive +# chainloader +1 +# +# title Linux +# root (hd0,1) +# kernel /vmlinuz root=/dev/hda2 ro +# + +# +# Put static boot stanzas before and/or after AUTOMAGIC KERNEL LIST + +### BEGIN AUTOMAGIC KERNELS LIST +## lines between the AUTOMAGIC KERNELS LIST markers will be modified +## by the debian update-grub script except for the default options below + +## DO NOT UNCOMMENT THEM, Just edit them to your needs + +## ## Start Default Options ## +## default kernel options +## default kernel options for automagic boot options +## If you want special options for specific kernels use kopt_x_y_z +## where x.y.z is kernel version. Minor versions can be omitted. +## e.g. kopt=root=/dev/hda1 ro +## kopt_2_6_8=root=/dev/hdc1 ro +## kopt_2_6_8_2_686=root=/dev/hdc2 ro +# kopt=root=UUID=766f85b2-0ecb-45b7-8ad9-b108f2eba4a2 ro + +## Setup crashdump menu entries +## e.g. crashdump=1 +# crashdump=0 + +## default grub root device +## e.g. groot=(hd0,0) +# groot=(hd0,0) + +## should update-grub create alternative automagic boot options +## e.g. alternative=true +## alternative=false +# alternative=true + +## should update-grub lock alternative automagic boot options +## e.g. lockalternative=true +## lockalternative=false +# lockalternative=false + +## additional options to use with the default boot option, but not with the +## alternatives +## e.g. defoptions=vga=791 resume=/dev/hda5 +# defoptions=quiet splash + +## should update-grub lock old automagic boot options +## e.g. lockold=false +## lockold=true +# lockold=false + +## Xen hypervisor options to use with the default Xen boot option +# xenhopt= + +## Xen Linux kernel options to use with the default Xen boot option +# xenkopt=console=tty0 + +## altoption boot targets option +## multiple altoptions lines are allowed +## e.g. altoptions=(extra menu suffix) extra boot options +## altoptions=(recovery) single +# altoptions=(recovery mode) single + +## controls how many kernels should be put into the menu.lst +## only counts the first occurence of a kernel, not the +## alternative kernel options +## e.g. howmany=all +## howmany=7 +# howmany=all + +## should update-grub create memtest86 boot option +## e.g. memtest86=true +## memtest86=false +# memtest86=true + +## should update-grub adjust the value of the default booted system +## can be true or false +# updatedefaultentry=false + +## ## End Default Options ## + +title XOmB +kernel /boot/xomb +#module /your/module +### END PITTGEEKS AUTOMAGIC KERNELS LIST diff --git a/build/iso/boot/grub/stage2_eltorito b/build/iso/boot/grub/stage2_eltorito new file mode 100644 index 0000000000000000000000000000000000000000..0fbaee7aee72b92cd7e65ebf6573e8e1b03cc468 GIT binary patch literal 110132 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 1 && format[1] is '{') + const size_t ExtractString = 2 + ExtractString!(format[2 .. $]); + else + const size_t ExtractString = 0; + } + else + const size_t ExtractString = 1 + ExtractString!(format[1 .. $]); + } + + // Extracts the format string and returns the length of that string. + template ExtractFormatStringImpl(char[] format) + { + static assert(format.length !is 0, "Unterminated format specifier"); + + static if(format[0] is '}') + const ExtractFormatStringImpl = 0; + else + const ExtractFormatStringImpl = 1 + ExtractFormatStringImpl!(format[1 .. $]); + } + + // This template compares the format given (eg {x} would be "x") against the type of the argument passed. + template CheckFormatAgainstType(char[] rawFormat, size_t idx, T) + { + const char[] format = rawFormat[1 .. idx]; + + static if(isIntType!(T)) + { + static assert(format == "" || format == "x" || format == "X" || format == "u" || format == "U", + "Invalid integer format specifier '" ~ format ~ "'"); + } + + // This is an inherited attribute to describe the length of the format string + const size_t res = idx; + } + + // This template will compare a format with a type. + template ExtractFormatString(char[] format, T) + { + const ExtractFormatString = CheckFormatAgainstType!(format, ExtractFormatStringImpl!(format), T).res; + } + + // This will get the length of a single command + template ExtractCommandStringImpl(char[] format) + { + static if (format.length == 0 || format[0] is '}') + { + const int ExtractCommandStringImpl = 0; + } + else + { + const int ExtractCommandStringImpl = 1 + ExtractCommandStringImpl!(format[1..$]); + } + } + + // This template will extract a command string, or set of command strings + template ExtractCommandString(char[] format) + { + const ExtractCommandString = ExtractCommandStringImpl!(format); + } + + // This template will take a string 's' and convert any '{{' to a single '{'. + // This is done after parsing the format string. + template StripDoubleLeftBrace(char[] s) + { + static if(s.length is 0) + const char[] StripDoubleLeftBrace = ""; + else static if(s.length is 1) + const char[] StripDoubleLeftBrace = s; + else + { + static if(s[0 .. 2] == "{{") + const char[] StripDoubleLeftBrace = "{" ~ StripDoubleLeftBrace!(s[2 .. $]); + else + const char[] StripDoubleLeftBrace = s[0] ~ StripDoubleLeftBrace!(s[1 .. $]); + } + } + + // Generates the code to print the string. + template MakePrintString(char[] s) + { + const char[] MakePrintString = "printString(\"" ~ StripDoubleLeftBrace!(s) ~ "\", \"\");\n"; + } + + // This template will generate the code to print out the string. + template MakePrintOther(T, char[] fmt, size_t idx) + { + static if(isIntType!(T)) + const char[] MakePrintOther = "printInt(args[" ~ idx.stringof ~ "], \"" ~ fmt ~ "\");\n"; + else static if(isCharType!(T)) + const char[] MakePrintOther = "printChar(args[" ~ idx.stringof ~ "], \"" ~ fmt ~ "\");\n"; + else static if(isStringType!(T)) + const char[] MakePrintOther = "printString(args[" ~ idx.stringof ~ "], \"" ~ fmt ~ "\");\n"; + else static if(isFloatType!(T)) + const char[] MakePrintOther = "printFloat(args[" ~ idx.stringof ~ "], \"" ~ fmt ~ "\");\n"; + else static if(isPointerType!(T)) + const char[] MakePrintOther = "printPointer(args[" ~ idx.stringof ~ "], \"" ~ fmt ~ "\");\n"; + else static if(isArrayType!(T)) + const char[] MakePrintOther = "printArray(args[" ~ idx.stringof ~ "], true, false);\n"; + else + static assert(false, "I don't know how to handle argument " ~ idx.stringof ~ " of type '" ~ T.stringof ~ "'."); + } + + // For the !fg command + template MakePrintCommand_fg(char[] format) + { + static if (format.length <= 1) + { + static assert(false, "Not enough parameters to the !fg command."); + } + else + { + const char[] MakePrintCommand_fg = "Console.setForeColor(Color." ~ format[1..$] ~ ");\n"; + } + } + + // For the !bg command + template MakePrintCommand_bg(char[] format) + { + static if (format.length <= 1) + { + static assert(false, "Not enough parameters to the !bg command."); + } + else + { + const char[] MakePrintCommand_bg = "Console.setBackColor(Color." ~ format[1..$] ~ ");\n"; + } + } + + template MakePrintCommand_pos(char[] format) + { + static if (format.length <= 3) + { + static assert(false, "Not enough parameters to the !pos command. USAGE: {!pos:x,y} where x and y are integers."); + } + else + { + const char[] MakePrintCommand_pos = "Console.setPosition(" ~ format[1..$] ~ ");\n"; + } + } + + // Output code to do the command. + template MakePrintCommandGenerate(char[] format) + { + static if (format.length >= 3 && format[0..3] == "cls") + { + const char[] MakePrintCommandGenerate = "Console.clearScreen();\n"; + } + else static if (format.length >= 2 && format[0..2] == "fg") + { + const char[] MakePrintCommandGenerate = MakePrintCommand_fg!(format[2..$]); + } + else static if (format.length >= 2 && format[0..2] == "bg") + { + const char[] MakePrintCommandGenerate = MakePrintCommand_bg!(format[2..$]); + } + else static if (format.length >= 3 && format[0..3] == "pos") + { + const char[] MakePrintCommandGenerate = MakePrintCommand_pos!(format[3..$]); + } + else + { + static assert(false, "Unknown Command, !" ~ format ~ ", for kprintf."); + } + } + + // Finds the length of the command string + template ExtractCommand(char[] format) + { + static if (format.length == 0 || format[0] is '}' || format[0] is '!') + { + const ExtractCommand = 0; + } + else + { + const ExtractCommand = 1 + ExtractCommand!(format[1..$]); + } + } + + // This template will take everything up to a ! or a } and generate the code for that command + template MakePrintCommandImpl(char[] format) + { + static if (format.length == 0) + { + const char[] res = ""; + } + else + { + static if (format[0] is '!') + { + const char[] res = MakePrintCommandImpl!(format[1..$]).res; + } + else + { + const lengthOfString = ExtractCommand!(format); + + const char[] res = MakePrintCommandGenerate!(format[0..lengthOfString]) ~ + MakePrintCommandImpl!(format[lengthOfString..$]).res; + } + } + } + + // commands: !cls, !fg:color, !bg:color + + // This template parses the command string (excluding the initial !) and generates the commands necessary. + template MakePrintCommand(char[] format) + { + const char[] MakePrintCommand = MakePrintCommandImpl!(format).res; + } + + // This template implements the logic behind format extraction. + template ConvertFormatImpl(char[] format, size_t argIdx, Types...) + { + static if(format.length == 0) + { + static assert(argIdx == Types.length, "More parameters than format specifiers"); + const char[] res = ""; + } + else + { + // Look for a token that starts with a left curly brace that would signify a format specifier. + static if (format[0] is '{' && (!(format.length > 1 && (format[1] is '{' || format[1] is '!')))) + { + // We have a format specifier, but no arguments to convert? + static assert(argIdx < Types.length, "More format specifiers than parameters"); + + // We will convert the string and generate code for the print. + + // Get the format string + const lengthOfString = ExtractFormatString!(format, Types[argIdx]); + + // Generate the code and recall this template. + const char[] res = MakePrintOther!(Types[argIdx], format[1 .. lengthOfString] , argIdx) ~ + ConvertFormatImpl!(format[lengthOfString + 1 .. $], argIdx + 1, Types).res; + } + else static if (format[0] is '{' && format.length > 1 && format[1] is '!') + { + // Command Token found, acts very similarly to a normal format specifier, expect it doesn't compare the types from the arguments. + const lengthOfString = ExtractCommandString!(format); + + // Generate the code and recall this template. + const char[] res = MakePrintCommand!(format[2..lengthOfString]) + ~ ConvertFormatImpl!(format[lengthOfString + 1 .. $], argIdx, Types).res; + } + else + { + // We want to know how long of a string we can print out without intervention + const lengthOfString = ExtractString!(format); + + // Then we can generate the code to print it out with the console and recall this template. + const char[] res = MakePrintString!(format[0..lengthOfString]) ~ + ConvertFormatImpl!(format[lengthOfString..$], argIdx, Types).res; + } + } + } + + // This template is the core routine. It will take the format and the arguments and generate the code to logically print out the string. + template ConvertFormat(char[] format, Types...) + { + const char[] ConvertFormat = ConvertFormatImpl!(format, 0, Types).res; + } +} + + + diff --git a/kernel/core/util.d b/kernel/core/util.d new file mode 100644 index 0000000..27b1faa --- /dev/null +++ b/kernel/core/util.d @@ -0,0 +1,383 @@ +module kernel.core.util; + +/** +This method checks to see if the value stored in the bit number declared +by the input variable "bit" in the flag declared by the input +variable "flags" is set. Returns a 1 if it is set, returns a 0 if it is not set. + Params: + flags = The flags from the multiboot header the kernel wishes to check. + bit = The number of the bit the kernel would like to check for data. + Returns: Whether the bit "bit" in "flags" has a value (1 if it is set, 0 if it is not) +*/ +uint CHECK_FLAG(uint flags, uint bit) +{ + return ((flags) & (1 << (bit))); +} + +/** +Given a struct type, gives a tuple of strings of the names of fields in the struct. +*/ +public template FieldNames(S, int idx = 0) +{ + static if(idx >= S.tupleof.length) + alias Tuple!() FieldNames; + else + alias Tuple!(GetLastName!(S.tupleof[idx].stringof), FieldNames!(S, idx + 1)) FieldNames; +} + +private template GetLastName(char[] fullName, int idx = fullName.length - 1) +{ + static if(idx < 0) + const char[] GetLastName = fullName; + else static if(fullName[idx] == '.') + const char[] GetLastName = fullName[idx + 1 .. $]; + else + const char[] GetLastName = GetLastName!(fullName, idx - 1); +} + +template Tuple(T...) +{ + alias T Tuple; +} + +template Bitfield(alias data, Args...) +{ + static assert(!(Args.length & 1), "Bitfield arguments must be an even number"); + const char[] Bitfield = BitfieldShim!((typeof(data)).stringof, data, Args).Ret; +} + +// Odd bug in D templates -- putting "data.stringof" as a template argument gives it the +// string of the type, rather than the string of the symbol. This shim works around that. +template BitfieldShim(char[] typeStr, alias data, Args...) +{ + const char[] Name = data.stringof; + const char[] Ret = BitfieldImpl!(typeStr, Name, 0, Args).Ret; +} + +template BitfieldImpl(char[] typeStr, char[] nameStr, int offset, Args...) +{ + static if(Args.length == 0) + const char[] Ret = ""; + else + { + const Name = Args[0]; + const Size = Args[1]; + const Mask = Bitmask!(Size); + + const char[] Getter = "public " ~ typeStr ~ " " ~ Name ~ "() { return ( " ~ + nameStr ~ " >> " ~ Itoh!(offset) ~ " ) & " ~ Itoh!(Mask) ~ "; }"; + + const char[] Setter = "public void " ~ Name ~ "(" ~ typeStr ~ " val) { " ~ + nameStr ~ " = (" ~ nameStr ~ " & " ~ Itoh!(~(Mask << offset)) ~ ") | ((val & " ~ + Itoh!(Mask) ~ ") << " ~ Itoh!(offset) ~ "); }"; + + const char[] Ret = Getter ~ Setter ~ BitfieldImpl!(typeStr, nameStr, offset + Size, Args[2 .. $]).Ret; + } +} + +template Itoa(long i) +{ + static if(i < 0) + const char[] Itoa = "-" ~ IntToStr!(-i, 10); + else + const char[] Itoa = IntToStr!(i, 10); +} + +template Itoh(long i) +{ + const char[] Itoh = "0x" ~ IntToStr!(i, 16); +} + +template Digits(long i) +{ + const char[] Digits = "0123456789abcdefghijklmnopqrstuvwxyz"[0 .. i]; +} + +template IntToStr(ulong i, int base) +{ + static if(i >= base) + const char[] IntToStr = IntToStr!(i / base, base) ~ Digits!(base)[i % base]; + else + const char[] IntToStr = "" ~ Digits!(base)[i % base]; +} + +template Bitmask(long size) +{ + const long Bitmask = (1L << size) - 1; +} + +template isStringType(T) +{ + const bool isStringType = is(T : char[]) || is(T : wchar[]) || is(T : dchar[]); +} + +/** +Sees if a type is char, wchar, or dchar. +*/ +template isCharType(T) +{ + const bool isCharType = is(T == char) || is(T == wchar) || is(T == dchar); +} + +/** +Sees if a type is a signed or unsigned byte, short, int, or long. +*/ +template isIntType(T) +{ + const bool isIntType = is(T == int) || is(T == uint) || is(T == long) || is(T == ulong) || + is(T == short) || is(T == ushort) || is(T == byte) || is(T == ubyte) /* || is(T == cent) || is(T == ucent) */; +} + +/** +Sees if a type is a signed or unsigned byte, short, int, or long. +*/ +template isUnsignedIntType(T) +{ + const bool isUnsignedIntType = is(T == uint) || is(T == ulong) || + is(T == ushort) || is(T == ubyte) /* || is(T == ucent) */; +} + +/** +Sees if a type is a signed or unsigned byte, short, int, or long. +*/ +template isSignedIntType(T) +{ + const bool isSignedIntType = is(T == int) || is(T == long) || + is(T == short) || is(T == byte) /* || is(T == cent) */; +} + + + + + +/** +Sees if a type is float, double, or real. +*/ +template isFloatType(T) +{ + const bool isFloatType = is(T == float) || is(T == double) || is(T == real); +} + +/** +Sees if a type is an array. +*/ +template isArrayType(T) +{ + const bool isArrayType = false; +} + +template isArrayType(T : T[]) +{ + const bool isArrayType = true; +} + +/** +Sees if a type is an associative array. +*/ +template isAAType(T) +{ + const bool isAAType = is(typeof(T.init.values[0])[typeof(T.init.keys[0])] == T); +} + +/** +Sees if a type is a pointer. +*/ +template isPointerType(T) +{ + const bool isPointerType = false; +} + +template isPointerType(T : T*) +{ + const bool isPointerType = true; +} + +/** +Get to the bottom of any chain of typedefs! Returns the first non-typedef'ed type. +*/ +template realType(T) +{ + static if(is(T Base == typedef) || is(T Base == enum)) + alias realType!(Base) realType; + else + alias T realType; +} + +/** +See if a character is a lowercase character. +*/ +template IsLower(char c) +{ + const bool IsLower = c >= 'a' && c <= 'z'; +} + +/** +See if a character is an uppercase character. +*/ +template IsUpper(char c) +{ + const bool IsUpper = c >= 'A' && c <= 'Z'; +} + +/** +Convert a character or string to lowercase. +*/ +template ToLower(char c) +{ + const char ToLower = IsUpper!(c) ? c + ('a' - 'A') : c; +} + +/// ditto +template ToLower(char[] s) +{ + static if(s.length == 0) + const ToLower = ""c; + else + const ToLower = ToLower!(s[0]) ~ s[1 .. $]; +} + +/** +Convert a character or string to uppercase. +*/ +template ToUpper(char c) +{ + const char ToUpper = IsLower!(c) ? c - ('a' - 'A') : c; +} + +/// ditto +template ToUpper(char[] s) +{ + static if(s.length == 0) + const ToUpper = ""c; + else + const ToUpper = ToUpper!(s[0]) ~ s[1 .. $]; +} + +/** +Capitalize a word so that the first letter is capital. +*/ +template Capitalize(char[] s) +{ + static if(s.length == 0) + const char[] Capitalize = ""c; + else + const char[] Capitalize = ToUpper!(s[0]) ~ ToLower!(s[1 .. $]); +} + +/** +Compile-time map. Takes a template "function" which should take a single argument +of the type of the elements of the list of values that follows. Resolves to a tuple. +*/ +template Map(alias Templ, List...) +{ + static if(List.length == 0) + alias Tuple!() Map; + else + alias Tuple!(Templ!(List[0]), Map!(Templ, List[1 .. $])) Map; +} + +/** +Compile-time reduce. Takes a template "function" that should take two arguments of the type +of the elements of the list of values that follows. The list must be at least one element +long. Resolves to a single value of the type that the template function returns. +*/ +template Reduce(alias Templ, List...) +{ + static assert(List.length > 0, "Reduce must be called on a list of at least one element"); + + static if(is(List[0])) + { + static if(List.length == 1) + alias List[0] Reduce; + else + alias Reduce!(Templ, Tuple!(Templ!(List[0], List[1]), List[2 .. $])) Reduce; + } + else + { + static if(List.length == 1) + const Reduce = List[0]; + else + const Reduce = Reduce!(Templ, Tuple!(Templ!(List[0], List[1]), List[2 .. $])); + } +} + +/** +Compile-time range. Given lower and upper bound, yields a tuple of integers in the +range [min, max). +*/ +template Range(uint min, uint max) +{ + static if(min >= max) + alias Tuple!() Range; + else + alias Tuple!(min, Range!(min + 1, max)) Range; +} + +/// ditto +template Range(uint max) +{ + alias Range!(0, max) Range; +} + +/** +Compile time metafunction meant to be used with Reduce. Concatenates its first two +arguments and resolves to the result of that concatentation. +*/ +template Cat(T...) +{ + const Cat = T[0] ~ T[1]; +} + + + + + + + + + + + +// standard functions + + +/** +This function converts an integer to a string, depending on the base passed in. + Params: + buf = The function will save the translated string into this character array. + base = The base of the integer value. If "d," it will be assumed to be decimal. If "x," the integer + will be hexadecimal. + d = The integer to translate. + Returns: The translated string in a character array. +*/ +char[] itoa(char[] buf, char base, long d) +{ + size_t p = buf.length - 1; + size_t startIdx = 0; + ulong ud = d; + bool negative = false; + + int divisor = 10; + + // If %d is specified and D is minus, put `-' in the head. + if(base == 'd' && d < 0) + { + negative = true; + ud = -d; + } + else if(base == 'x') + divisor = 16; + + // Divide UD by DIVISOR until UD == 0. + do + { + int remainder = ud % divisor; + buf[p--] = (remainder < 10) ? remainder + '0' : remainder + 'a' - 10; + } + while (ud /= divisor) + + if(negative) + buf[p--] = '-'; + + return buf[p + 1 .. $]; +} diff --git a/kernel/dev/console.d b/kernel/dev/console.d new file mode 100644 index 0000000..f954a4f --- /dev/null +++ b/kernel/dev/console.d @@ -0,0 +1,191 @@ +// This implements a console (text-mode) VGA driver. + +module kernel.dev.console; + +// This contains the hexidecimal values for various colors for printing to the screen. +enum Color : ubyte +{ + Black = 0x00, + Blue = 0x01, + Green = 0x02, + Cyan = 0x03, + Red = 0x04, + Magenta = 0x05, + Yellow = 0x06, + LightGray = 0x07, + Gray = 0x08, + LightBlue = 0x09, + LightGreen = 0x0A, + LightCyan = 0x0B, + LightRed = 0x0C, + LightMagenta = 0x0D, + LightYellow = 0x0E, + White = 0x0F +} + +// This is the true interface to the console +struct Console +{ +static: +public: + + // The number of columns and lines on the screen. + const uint COLUMNS = 80; + const uint LINES = 24; + + // The default color. + const ubyte DEFAULTCOLORS = Color.LightGray; + + ubyte* videoMemoryLocation = cast(ubyte*)0xFFFFFFFF800B8000; + + // The cursor position + private int xpos = 0; + private int ypos = 0; + + // The current color + private ubyte colorAttribute = DEFAULTCOLORS; + + // The width of a tab + const auto TABSTOP = 4; + + // This method will clear the screen and return the cursor to (0,0). + void clearScreen() + { + int i; + + for (i=0; i < COLUMNS * LINES * 2; i++) + { + volatile *(videoMemoryLocation + i) = 0; + } + + xpos = 0; + ypos = 0; + } + + // This method will return the current location of the cursor + void getPosition(out int x, out int y) + { + x = xpos; + y = ypos; + } + + // This method will set the current location of the cursor to the x and y given. + void setPosition(int x, int y) + { + if (x < 0) { x = 0; } + if (y < 0) { y = 0; } + if (x >= COLUMNS) { x = COLUMNS - 1; } + if (y >= LINES) { y = LINES - 1; } + + xpos = x; + ypos = y; + } + + // This method will post the character to the screen at the current location. + void putChar(char c) + { + if (c == '\t') + { + // Insert a tab. + xpos += TABSTOP; + } + else if (c != '\n' && c != '\r') + { + // Set the current piece of video memory to the character to print. + volatile *(videoMemoryLocation + (xpos + ypos * COLUMNS) * 2) = c & 0xFF; + volatile *(videoMemoryLocation + (xpos + ypos * COLUMNS) * 2 + 1) = colorAttribute; + + // increase the cursor position + xpos++; + } + + // if you have reached the end of the line, or printing a newline, increase the y position + if (c == '\n' || c == '\r' || xpos >= COLUMNS) + { + xpos = 0; + ypos++; + + if (ypos >= LINES) + { + scrollDisplay(1); + } + } + } + + // This mehtod will post a string to the screen at the current location. + void putString(char[] s) + { + foreach(c; s) + { + putChar(c); + } + } + + // This function sets the console colors back to their defaults. + void resetColors() + { + colorAttribute = DEFAULTCOLORS; + } + + // This function will set the text foreground to a new color. + void setForeColor(Color newColor) + { + colorAttribute = (colorAttribute & 0xf0) | newColor; + } + + // This function will set the text background to a new color. + void setBackColor(Color newColor) + { + colorAttribute = (colorAttribute & 0x0f) | (newColor << 4); + } + + // This function will set both the foreground and background colors. + void setColors(Color foreColor, Color backColor) + { + colorAttribute = (foreColor & 0x0f) | (backColor << 4); + } + + // This function will scroll the entire screen. + void scrollDisplay(uint numLines) + { + // obviously, scrolling all lines results in a cleared display. Use the faster function. + if (numLines >= LINES) + { + clearScreen(); + return; + } + + int cury = 0; + int offset1 = 0; + int offset2 = numLines * COLUMNS; + + // Go through and shift the correct amount. + for ( ; cury <= LINES - numLines; cury++) + { + for (int curx = 0; curx < COLUMNS; curx++) + { + *(videoMemoryLocation + (curx + offset1) * 2) = *(videoMemoryLocation + (curx + offset1 + offset2) * 2); + *(videoMemoryLocation + (curx + offset1) * 2 + 1) = *(videoMemoryLocation + (curx + offset1 + offset2) * 2 + 1); + } + + offset1 += COLUMNS; + } + + // clear the remaining lines + for (; cury <= LINES; cury++) + { + for (int curx = 0; curx < COLUMNS; curx++) + { + *(videoMemoryLocation + (curx + offset1) * 2) = 0x00; + *(videoMemoryLocation + (curx + offset1) * 2 + 1) = 0x00; + } + } + + ypos -= numLines; + + if (ypos < 0) + { + ypos = 0; + } + } +} diff --git a/kernel/runtime/dstubs.d b/kernel/runtime/dstubs.d new file mode 100644 index 0000000..a83a4e1 --- /dev/null +++ b/kernel/runtime/dstubs.d @@ -0,0 +1,1014 @@ +// This is the stubbed out runtime for D. + +// It is magical and full of nondescriptive mystical creatures. + +module kernel.core.dstubs; + +// some utility functions and routines for the runtime +import kernel.runtime.util; + +// for printing out runtime errors +import kernel.core.kprintf; + +// magical gcc business (BEWARE THE MAGICAL EMPTY FILE!!!) +static import gcc.builtins; + +extern(C) +{ + +alias gcc.builtins.__builtin_alloca alloca; + +private +{ + struct Array + { + size_t length; + byte *data; + } + + struct aaA + { + aaA *left; + aaA *right; + hash_t hash; + /* key */ + /* value */ + } + + struct BB + { + aaA*[] b; + size_t nodes; + } + + struct AA + { + BB* a; + } + + alias long ArrayRet_t; + extern(D) typedef int delegate(void *) aa_dg_t; + extern(D) typedef int delegate(void *, void *) aa_dg2_t; + extern(D) typedef int delegate(void *) array_dg_t; + extern(D) typedef int delegate(void *, void *) array_dg2_t; + + enum BlkAttr : uint + { + FINALIZE = 0b0000_0001, + NO_SCAN = 0b0000_0010, + NO_MOVE = 0b0000_0100, + ALL_BITS = 0b1111_1111 + } + + template Stub(char[] signature) + { + const char[] Stub = signature ~ " { assert(false, \"Undefined runtime stub executed: " ~ signature ~ "\"); }"; + } +} + +/************************************************** + Random stubs (they'll go somewhere eventually) +**************************************************/ + +mixin(Stub!("void abort()")); +mixin(Stub!("bool rt_isHalting()")); +mixin(Stub!("bool runModuleUnitTests()")); +mixin(Stub!("void _d_monitordelete(Object h, bool det = true)")); + +/****************************************** + * Given a pointer: + * If it is an Object, return that Object. + * If it is an interface, return the Object implementing the interface. + * If it is null, return null. + * Else, undefined crash + */ + +Object _d_toObject(void* p) +{ Object o; + + if (p) + { + o = cast(Object)p; + ClassInfo oc = o.classinfo; + Interface *pi = **cast(Interface ***)p; + + /* Interface.offset lines up with ClassInfo.name.ptr, + * so we rely on pointers never being less than 64K, + * and Objects never being greater. + */ + if (pi.offset < 0x10000) + { + //printf("\tpi.offset = %d\n", pi.offset); + o = cast(Object)(p - pi.offset); + } + } + return o; +} + + +/************************************* + * Attempts to cast Object o to class c. + * Returns o if successful, null if not. + */ + +Object _d_interface_cast(void* p, ClassInfo c) +{ Object o; + + //printf("_d_interface_cast(p = %p, c = '%.*s')\n", p, c.name); + if (p) + { + Interface *pi = **cast(Interface ***)p; + + //printf("\tpi.offset = %d\n", pi.offset); + o = cast(Object)(p - pi.offset); + return _d_dynamic_cast(o, c); + } + return o; +} + +Object _d_dynamic_cast(Object o, ClassInfo c) +{ ClassInfo oc; + size_t offset = 0; + + //printf("_d_dynamic_cast(o = %p, c = '%.*s')\n", o, c.name); + + if (o) + { + oc = o.classinfo; + if (_d_isbaseof2(oc, c, offset)) + { + //printf("\toffset = %d\n", offset); + o = cast(Object)(cast(void*)o + offset); + } + else + o = null; + } + //printf("\tresult = %p\n", o); + return o; +} + +int _d_isbaseof2(ClassInfo oc, ClassInfo c, inout size_t offset) +{ int i; + + if (oc is c) + return 1; + do + { + if (oc.base is c) + return 1; + for (i = 0; i < oc.interfaces.length; i++) + { + ClassInfo ic; + + ic = oc.interfaces[i].classinfo; + if (ic is c) + { offset = oc.interfaces[i].offset; + return 1; + } + } + for (i = 0; i < oc.interfaces.length; i++) + { + ClassInfo ic; + + ic = oc.interfaces[i].classinfo; + if (_d_isbaseof2(ic, c, offset)) + { offset = oc.interfaces[i].offset; + return 1; + } + } + oc = oc.base; + } while (oc); + return 0; +} + +int _d_isbaseof(ClassInfo oc, ClassInfo c) +{ int i; + + if (oc is c) + return 1; + do + { + if (oc.base is c) + return 1; + for (i = 0; i < oc.interfaces.length; i++) + { + ClassInfo ic; + + ic = oc.interfaces[i].classinfo; + if (ic is c || _d_isbaseof(ic, c)) + return 1; + } + oc = oc.base; + } while (oc); + return 0; +} + +/********************************* + * Find the vtbl[] associated with Interface ic. + */ + +void *_d_interface_vtbl(ClassInfo ic, Object o) +{ int i; + ClassInfo oc; + + //printf("__d_interface_vtbl(o = %p, ic = %p)\n", o, ic); + + assert(o); + + oc = o.classinfo; + for (i = 0; i < oc.interfaces.length; i++) + { + ClassInfo oic; + + oic = oc.interfaces[i].classinfo; + if (oic is ic) + { + return cast(void *)oc.interfaces[i].vtbl; + } + } + assert(0); +} + +int _d_obj_eq(Object o1, Object o2) +{ + return o1 is o2 || (o1 && o1.opEquals(o2)); +} + +int _d_obj_cmp(Object o1, Object o2) +{ + return o1.opCmp(o2); +} + +int _d_switch_string(char[][] table, char[] ca) +{ + int low; + int high; + int mid; + int c; + char[] pca; + + low = 0; + high = table.length; + + if (high && + ca.length >= table[0].length && + ca.length <= table[high - 1].length) + { + // Looking for 0 length string, which would only be at the beginning + if (ca.length == 0) + return 0; + + char c1 = ca[0]; + + // Do binary search + while (low < high) + { + mid = (low + high) >> 1; + pca = table[mid]; + c = ca.length - pca.length; + if (c == 0) + { + c = cast(ubyte)c1 - cast(ubyte)pca[0]; + if (c == 0) + { + c = memcmp(ca.ptr, pca.ptr, ca.length); + if (c == 0) + { + return mid; + } + } + } + if (c < 0) + { + high = mid; + } + else + { + low = mid + 1; + } + } + } + + return -1; // not found +} + +int _d_switch_ustring(wchar[][] table, wchar[] ca) +{ + int low; + int high; + int mid; + int c; + wchar[] pca; + + low = 0; + high = table.length; + + // Do binary search + while (low < high) + { + mid = (low + high) >> 1; + pca = table[mid]; + c = ca.length - pca.length; + if (c == 0) + { + c = memcmp(ca.ptr, pca.ptr, ca.length * wchar.sizeof); + if (c == 0) + { + return mid; + } + } + if (c < 0) + { + high = mid; + } + else + { + low = mid + 1; + } + } + + return -1; // not found +} + +int _d_switch_dstring(dchar[][] table, dchar[] ca) +{ + int low; + int high; + int mid; + int c; + dchar[] pca; + + low = 0; + high = table.length; + + // Do binary search + while (low < high) + { + mid = (low + high) >> 1; + pca = table[mid]; + c = ca.length - pca.length; + if (c == 0) + { + c = memcmp(ca.ptr, pca.ptr, ca.length * dchar.sizeof); + if (c == 0) + { + return mid; + } + } + if (c < 0) + { + high = mid; + } + else + { + low = mid + 1; + } + } + + return -1; // not found +} + +/************************************************** + Lifetime stubs +**************************************************/ + +mixin(Stub!("Object _d_newclass(ClassInfo ci)")); +mixin(Stub!("void _d_delinterface(void** p)")); +mixin(Stub!("void _d_delclass(Object* p)")); +mixin(Stub!("Array _d_newarrayT(TypeInfo ti, size_t length)")); +mixin(Stub!("Array _d_newarrayiT(TypeInfo ti, size_t length)")); +mixin(Stub!("void[] _d_newarraymTp(TypeInfo ti, int ndims, size_t* pdim)")); +mixin(Stub!("void[] _d_newarraymiTp(TypeInfo ti, int ndims, size_t* pdim)")); +mixin(Stub!("void _d_delarray(Array *p)")); +mixin(Stub!("void _d_delmemory(void* *p)")); +mixin(Stub!("void _d_callfinalizer(void* p)")); +mixin(Stub!("void rt_finalize(void* p, bool det = true)")); +mixin(Stub!("byte[] _d_arraysetlengthT(TypeInfo ti, size_t newlength, Array *p)")); +mixin(Stub!("byte[] _d_arraysetlengthiT(TypeInfo ti, size_t newlength, Array *p)")); +mixin(Stub!("Array _d_arrayappendT(TypeInfo ti, Array *px, byte[] y)")); +mixin(Stub!("byte[] _d_arrayappendcTp(TypeInfo ti, inout byte[] x, void *argp)")); +mixin(Stub!("byte[] _d_arraycatT(TypeInfo ti, byte[] x, byte[] y)")); +mixin(Stub!("byte[] _d_arraycatnT(TypeInfo ti, uint n, ...)")); +mixin(Stub!("Array _adDupT(TypeInfo ti, Array a)")); + +/************************************************** + GC stubs +**************************************************/ + +mixin(Stub!("void gc_init()")); +mixin(Stub!("void gc_term()")); +mixin(Stub!("void gc_enable()")); +mixin(Stub!("void gc_disable()")); +mixin(Stub!("void gc_collect()")); +mixin(Stub!("uint gc_getAttr( void* p )")); +mixin(Stub!("uint gc_setAttr( void* p, uint a )")); +mixin(Stub!("uint gc_clrAttr( void* p, uint a )")); +mixin(Stub!("void* gc_malloc( size_t sz, uint ba = 0 )")); +mixin(Stub!("void* gc_calloc( size_t sz, uint ba = 0 )")); +mixin(Stub!("void* gc_realloc( void* p, size_t sz, uint ba = 0 )")); +mixin(Stub!("size_t gc_extend( void* p, size_t mx, size_t sz )")); +mixin(Stub!("void gc_free( void* p )")); +mixin(Stub!("size_t gc_sizeOf( void* p )")); +mixin(Stub!("void gc_addRoot( void* p )")); +mixin(Stub!("void gc_addRange( void* p, size_t sz )")); +mixin(Stub!("void gc_removeRoot( void *p )")); +mixin(Stub!("void gc_removeRange( void *p )")); +mixin(Stub!("bool onCollectResource( Object obj )")); + +/************************************************** + Exception stubs +**************************************************/ + +void _d_assert( char[] file, uint line ) +{ + onAssertError( file, line ); +} + +void _d_assert_msg( char[] msg, char[] file, uint line ) +{ + onAssertErrorMsg( file, line, msg ); +} + +void _d_array_bounds( char[] file, uint line ) +{ + onArrayBoundsError( file, line ); +} + +void _d_switch_error( char[] file, uint line ) +{ + onSwitchError( file, line ); +} + +private void onAssertError(char[] file, size_t line) +{ + kprintfln!("Error in {}, line {}: assertion failed.")(file, line); + asm { l: hlt; jmp l; } +} + +private void onAssertErrorMsg(char[] file, size_t line, char[] msg) +{ + kprintfln!("Error in {}, line {}: assertion failed: \"{}\"")(file, line, msg); + asm { l: hlt; jmp l; } +} + +private void onArrayBoundsError(char[] file, size_t line) +{ + kprintfln!("Error in {}, line {}: array index out of bounds.")(file, line); + asm { l: hlt; jmp l; } +} + +private void onSwitchError(char[] file, size_t line) +{ + kprintfln!("Error in {}, line {}: switch has no case or default to handle the switched-upon value.")(file, line); + asm { l: hlt; jmp l; } +} + +mixin(Stub!("void onFinalizeError( ClassInfo info, Exception ex )")); +mixin(Stub!("void onOutOfMemoryError()")); +mixin(Stub!("void onUnicodeError( char[] msg, size_t idx )")); + +/************************************************** + DEH and Unwind stubs +**************************************************/ + +mixin(Stub!("void _gdc_cleanupException()")); +mixin(Stub!("void _d_throw(Object obj)")); +mixin(Stub!("int __gdc_personality_v0()")); +mixin(Stub!("void _Unwind_RaiseException ()")); +mixin(Stub!("void _Unwind_ForcedUnwind ()")); +mixin(Stub!("void _Unwind_DeleteException ()")); +mixin(Stub!("void _Unwind_Resume()")); +mixin(Stub!("void _Unwind_Resume_or_Rethrow ()")); +mixin(Stub!("void _Unwind_Backtrace ()")); +mixin(Stub!("void _Unwind_GetGR ()")); +mixin(Stub!("void _Unwind_SetGR ()")); +mixin(Stub!("void _Unwind_GetIP ()")); +mixin(Stub!("void _Unwind_SetIP ()")); +mixin(Stub!("void _Unwind_GetCFA ()")); +mixin(Stub!("void *_Unwind_GetLanguageSpecificData ()")); +mixin(Stub!("void _Unwind_GetRegionStart ()")); +mixin(Stub!("void _Unwind_SjLj_RaiseException()")); +mixin(Stub!("void _Unwind_SjLj_ForcedUnwind()")); +mixin(Stub!("void _Unwind_SjLj_Resume ()")); +mixin(Stub!("void _Unwind_GetDataRelBase ()")); +mixin(Stub!("void _Unwind_GetTextRelBase ()")); +mixin(Stub!("uint size_of_encoded_value (ubyte encoding)")); +mixin(Stub!("void base_of_encoded_value ()")); +mixin(Stub!("void read_uleb128()")); +mixin(Stub!("void read_sleb128()")); +mixin(Stub!("void read_encoded_value_with_base()")); +mixin(Stub!("void read_encoded_value()")); + +/************************************************** + AA stubs +**************************************************/ + +mixin(Stub!("size_t _aaLen(AA aa)")); +mixin(Stub!("void *_aaGetp(AA* aa, TypeInfo keyti, size_t valuesize, void *pkey)")); +mixin(Stub!("void *_aaGetRvaluep(AA aa, TypeInfo keyti, size_t valuesize, void *pkey)")); +mixin(Stub!("void* _aaInp(AA aa, TypeInfo keyti, void *pkey)")); +mixin(Stub!("void _aaDelp(AA aa, TypeInfo keyti, void *pkey)")); +mixin(Stub!("Array _aaValues(AA aa, size_t keysize, size_t valuesize)")); +mixin(Stub!("AA _aaRehash(AA* paa, TypeInfo keyti)")); +mixin(Stub!("Array _aaKeys(AA aa, size_t keysize)")); +mixin(Stub!("int _aaApply(AA aa, size_t keysize, aa_dg_t dg)")); +mixin(Stub!("int _aaApply2(AA aa, size_t keysize, aa_dg2_t dg)")); +mixin(Stub!("BB* _d_assocarrayliteralTp(TypeInfo_AssociativeArray ti, size_t length, void *keys, void *values)")); + +/************************************************** + Array stubs +**************************************************/ + +mixin(Stub!("int _aApplycw1(char[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplycd1(char[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplywc1(wchar[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplywd1(wchar[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplydc1(dchar[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplydw1(dchar[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplycw2(char[] aa, array_dg2_t dg)")); +mixin(Stub!("int _aApplycd2(char[] aa, array_dg2_t dg)")); +mixin(Stub!("int _aApplywc2(wchar[] aa, array_dg2_t dg)")); +mixin(Stub!("int _aApplywd2(wchar[] aa, array_dg2_t dg)")); +mixin(Stub!("int _aApplydc2(dchar[] aa, array_dg2_t dg)")); +mixin(Stub!("int _aApplydw2(dchar[] aa, array_dg2_t dg)")); +mixin(Stub!("int _aApplyRcw1(char[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplyRcd1(char[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplyRwc1(wchar[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplyRwd1(wchar[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplyRdc1(dchar[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplyRdw1(dchar[] aa, array_dg_t dg)")); +mixin(Stub!("int _aApplyRcw2(char[] aa, array_dg2_t dg)")); +mixin(Stub!("int _aApplyRcd2(char[] aa, array_dg2_t dg)")); +mixin(Stub!("int _aApplyRwc2(wchar[] aa, array_dg2_t dg)")); +mixin(Stub!("int _aApplyRwd2(wchar[] aa, array_dg2_t dg)")); +mixin(Stub!("int _aApplyRdc2(dchar[] aa, array_dg2_t dg)")); +mixin(Stub!("int _aApplyRdw2(dchar[] aa, array_dg2_t dg)")); +mixin(Stub!("char[] _adSortChar(char[] a)")); +mixin(Stub!("wchar[] _adSortWchar(wchar[] a)")); + + +const ubyte[256] UTF8stride = +[ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0xFF,0xFF, +]; + +Array _adReverseChar(char[] a) +{ + if(a.length > 1) + { + char[6] tmp; + char[6] tmplo; + char* lo = a.ptr; + char* hi = &a[length - 1]; + + while (lo < hi) + { auto clo = *lo; + auto chi = *hi; + + if (clo <= 0x7F && chi <= 0x7F) + { + *lo = chi; + *hi = clo; + lo++; + hi--; + continue; + } + + uint stridelo = UTF8stride[clo]; + + uint stridehi = 1; + while ((chi & 0xC0) == 0x80) + { + chi = *--hi; + stridehi++; + assert(hi >= lo); + } + if (lo == hi) + break; + + if (stridelo == stridehi) + { + + memcpy(tmp.ptr, lo, stridelo); + memcpy(lo, hi, stridelo); + memcpy(hi, tmp.ptr, stridelo); + lo += stridelo; + hi--; + continue; + } + + /* Shift the whole array. This is woefully inefficient + */ + memcpy(tmp.ptr, hi, stridehi); + memcpy(tmplo.ptr, lo, stridelo); + memmove(lo + stridehi, lo + stridelo , (hi - lo) - stridelo); + memcpy(lo, tmp.ptr, stridehi); + memcpy(hi + cast(int) stridehi - cast(int) stridelo, tmplo.ptr, stridelo); + + lo += stridehi; + hi = hi - 1 + (cast(int) stridehi - cast(int) stridelo); + } + } + + Array aaa = *cast(Array*)(&a); + return aaa; +} + +Array _adReverseWchar(wchar[] a) +{ + if (a.length > 1) + { + wchar[2] tmp; + wchar* lo = a.ptr; + wchar* hi = &a[length - 1]; + + while (lo < hi) + { auto clo = *lo; + auto chi = *hi; + + if ((clo < 0xD800 || clo > 0xDFFF) && + (chi < 0xD800 || chi > 0xDFFF)) + { + *lo = chi; + *hi = clo; + lo++; + hi--; + continue; + } + + int stridelo = 1 + (clo >= 0xD800 && clo <= 0xDBFF); + + int stridehi = 1; + if (chi >= 0xDC00 && chi <= 0xDFFF) + { + chi = *--hi; + stridehi++; + assert(hi >= lo); + } + if (lo == hi) + break; + + if (stridelo == stridehi) + { int stmp; + + assert(stridelo == 2); + assert(stmp.sizeof == 2 * (*lo).sizeof); + stmp = *cast(int*)lo; + *cast(int*)lo = *cast(int*)hi; + *cast(int*)hi = stmp; + lo += stridelo; + hi--; + continue; + } + + /* Shift the whole array. This is woefully inefficient + */ + memcpy(tmp.ptr, hi, stridehi * wchar.sizeof); + memcpy(hi + cast(int) stridehi - cast(int) stridelo, lo, stridelo * wchar.sizeof); + memmove(lo + stridehi, lo + stridelo , (hi - (lo + stridelo)) * wchar.sizeof); + memcpy(lo, tmp.ptr, stridehi * wchar.sizeof); + + lo += stridehi; + hi = hi - 1 + (cast(int) stridehi - cast(int) stridelo); + } + } + + Array aaa = *cast(Array*)(&a); + return aaa; +} + +int _adCmpChar(Array a1, Array a2) +{ + version (Asm86) + { + asm + { naked ; + + push EDI ; + push ESI ; + + mov ESI,a1+4[4+ESP] ; + mov EDI,a2+4[4+ESP] ; + + mov ECX,a1[4+ESP] ; + mov EDX,a2[4+ESP] ; + + cmp ECX,EDX ; + jb GotLength ; + + mov ECX,EDX ; + + GotLength: + cmp ECX,4 ; + jb DoBytes ; + + // Do alignment if neither is dword aligned + test ESI,3 ; + jz Aligned ; + + test EDI,3 ; + jz Aligned ; + DoAlign: + mov AL,[ESI] ; //align ESI to dword bounds + mov DL,[EDI] ; + + cmp AL,DL ; + jnz Unequal ; + + inc ESI ; + inc EDI ; + + test ESI,3 ; + + lea ECX,[ECX-1] ; + jnz DoAlign ; + Aligned: + mov EAX,ECX ; + + // do multiple of 4 bytes at a time + + shr ECX,2 ; + jz TryOdd ; + + repe ; + cmpsd ; + + jnz UnequalQuad ; + + TryOdd: + mov ECX,EAX ; + DoBytes: + // if still equal and not end of string, do up to 3 bytes slightly + // slower. + + and ECX,3 ; + jz Equal ; + + repe ; + cmpsb ; + + jnz Unequal ; + Equal: + mov EAX,a1[4+ESP] ; + mov EDX,a2[4+ESP] ; + + sub EAX,EDX ; + pop ESI ; + + pop EDI ; + ret ; + + UnequalQuad: + mov EDX,[EDI-4] ; + mov EAX,[ESI-4] ; + + cmp AL,DL ; + jnz Unequal ; + + cmp AH,DH ; + jnz Unequal ; + + shr EAX,16 ; + + shr EDX,16 ; + + cmp AL,DL ; + jnz Unequal ; + + cmp AH,DH ; + Unequal: + sbb EAX,EAX ; + pop ESI ; + + or EAX,1 ; + pop EDI ; + + ret ; + } + } + else + { + int len; + int c; + + len = a1.length; + if (a2.length < len) + len = a2.length; + c = memcmp(cast(char *)a1.data, cast(char *)a2.data, len); + if (!c) + c = cast(int)a1.length - cast(int)a2.length; + return c; + } +} + +Array _adReverse(Array a, size_t szelem) +{ + if (a.length >= 2) + { + byte* tmp; + byte[16] buffer; + + void* lo = a.data; + void* hi = a.data + (a.length - 1) * szelem; + + tmp = buffer.ptr; + if (szelem > 16) + tmp = cast(byte*)alloca(szelem); + + for (; lo < hi; lo += szelem, hi -= szelem) + { + memcpy(tmp, lo, szelem); + memcpy(lo, hi, szelem); + memcpy(hi, tmp, szelem); + } + } + return a; +} + +int _adEq(Array a1, Array a2, TypeInfo ti) +{ + if(a1.length != a2.length) + return 0; // not equal + + auto sz = ti.tsize(); + auto p1 = a1.data; + auto p2 = a2.data; + + if(sz == 1) + // We should really have a ti.isPOD() check for this + return (memcmp(p1, p2, a1.length) == 0); + + for(size_t i = 0; i < a1.length; i++) + { + if(!ti.equals(p1 + i * sz, p2 + i * sz)) + return 0; // not equal + } + + return 1; // equal +} + +int _adCmp(Array a1, Array a2, TypeInfo ti) +{ + //printf("adCmp()\n"); + auto len = a1.length; + if (a2.length < len) + len = a2.length; + auto sz = ti.tsize(); + void *p1 = a1.data; + void *p2 = a2.data; + + if (sz == 1) + { // We should really have a ti.isPOD() check for this + auto c = memcmp(p1, p2, len); + if (c) + return c; + } + else + { + for (size_t i = 0; i < len; i++) + { + auto c = ti.compare(p1 + i * sz, p2 + i * sz); + if (c) + return c; + } + } + if (a1.length == a2.length) + return 0; + return (a1.length > a2.length) ? 1 : -1; +} + +Array _adSort(Array a, TypeInfo ti) +{ + static const uint Qsort_Threshold = 7; + + struct StackEntry { + byte *l; + byte *r; + } + + size_t elem_size = ti.tsize(); + size_t qsort_limit = elem_size * Qsort_Threshold; + + static assert(ubyte.sizeof == 1); + static assert(ubyte.max == 255); + + StackEntry[size_t.sizeof * 8] stack; // log2( size_t.max ) + StackEntry * sp = stack.ptr; + byte* lbound = cast(byte *) a.data; + byte* rbound = cast(byte *) a.data + a.length * elem_size; + byte* li = void; + byte* ri = void; + + while (1) + { + if (rbound - lbound > qsort_limit) + { + ti.swap(lbound, + lbound + ( + ((rbound - lbound) >>> 1) - + (((rbound - lbound) >>> 1) % elem_size) + )); + + li = lbound + elem_size; + ri = rbound - elem_size; + + if (ti.compare(li, ri) > 0) + ti.swap(li, ri); + if (ti.compare(lbound, ri) > 0) + ti.swap(lbound, ri); + if (ti.compare(li, lbound) > 0) + ti.swap(li, lbound); + + while (1) + { + do + li += elem_size; + while (ti.compare(li, lbound) < 0); + do + ri -= elem_size; + while (ti.compare(ri, lbound) > 0); + if (li > ri) + break; + ti.swap(li, ri); + } + ti.swap(lbound, ri); + if (ri - lbound > rbound - li) + { + sp.l = lbound; + sp.r = ri; + lbound = li; + } + else + { + sp.l = li; + sp.r = rbound; + rbound = ri; + } + ++sp; + } else { + // Use insertion sort + for (ri = lbound, li = lbound + elem_size; + li < rbound; + ri = li, li += elem_size) + { + for ( ; ti.compare(ri, ri + elem_size) > 0; + ri -= elem_size) + { + ti.swap(ri, ri + elem_size); + if (ri == lbound) + break; + } + } + if (sp != stack.ptr) + { + --sp; + lbound = sp.l; + rbound = sp.r; + } + else + return a; + } + } +} + +void[] _d_arraycast(size_t tsize, size_t fsize, void[] a) +{ + auto length = a.length; + auto nbytes = length * fsize; + + if(nbytes % tsize != 0) + throw new Exception("array cast misalignment"); + + length = nbytes / tsize; + *cast(size_t *)&a = length; // jam new length + return a; +} + +byte[] _d_arraycopy(size_t size, byte[] from, byte[] to) +{ + if(to.length != from.length) + throw new Exception("lengths don't match for array copy"); + else if(cast(byte *)to + to.length * size <= cast(byte *)from || cast(byte *)from + from.length * size <= cast(byte *)to) + memcpy(cast(byte *)to, cast(byte *)from, to.length * size); + else + throw new Exception("overlapping array copy"); + + return to; +} + +} diff --git a/kernel/runtime/gcc/builtins.d b/kernel/runtime/gcc/builtins.d new file mode 100644 index 0000000..749d8af --- /dev/null +++ b/kernel/runtime/gcc/builtins.d @@ -0,0 +1,41 @@ +/* GDC -- D front-end for GCC + Copyright (C) 2004 David Friedman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +/** + Declarations are automatically created by the compiler. All + declarations start with "__builtin_". Refer to _builtins.def in the + GCC source for a list of functions. Not all of the functions are + supported. + + In addition to built-in functions, the following types are defined. + + $(TABLE + $(TR $(TD ___builtin_va_list) $(TD The target's va_list type )) + $(TR $(TD ___builtin_Clong ) $(TD The D equivalent of the target's + C "long" type )) + $(TR $(TD ___builtin_Culong ) $(TD The D equivalent of the target's + C "unsigned long" type )) + $(TR $(TD ___builtin_machine_int ) $(TD Signed word-sized integer )) + $(TR $(TD ___builtin_machine_uint) $(TD Unsigned word-sized integer )) + $(TR $(TD ___builtin_pointer_int ) $(TD Signed pointer-sized integer )) + $(TR $(TD ___builtin_pointer_uint) $(TD Unsigned pointer-sized integer )) + ) +*/ + +module gcc.builtins; diff --git a/kernel/runtime/invariant.d b/kernel/runtime/invariant.d new file mode 100644 index 0000000..0b03e5c --- /dev/null +++ b/kernel/runtime/invariant.d @@ -0,0 +1,28 @@ + +/* + * Placed into the Public Domain + * written by Walter Bright + * www.digitalmars.com + */ + +void _d_invariant(Object o) +{ + ClassInfo c; + + //printf("__d_invariant(%p)\n", o); + + // BUG: needs to be filename/line of caller, not library routine + assert(o !is null); // just do null check, not invariant check + + c = o.classinfo; + + do + { + if(c.classInvariant) + { + (*c.classInvariant)(o); + } + + c = c.base; + } while(c) +} diff --git a/kernel/runtime/object.d b/kernel/runtime/object.d new file mode 100644 index 0000000..242d677 --- /dev/null +++ b/kernel/runtime/object.d @@ -0,0 +1,1053 @@ + +/** + * Part of the D programming language runtime library. + * Forms the symbols available to all D programs. Includes + * Object, which is the root of the class object hierarchy. + * + * This module is implicitly imported. + * Macros: + * WIKI = Phobos/Object + */ + +/* + * Copyright (C) 2004-2007 by Digital Mars, www.digitalmars.com + * Written by Walter Bright + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, in both source and binary form, subject to the following + * restrictions: + * + * o The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * o Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * o This notice may not be removed or altered from any source + * distribution. + */ + + +module object; + +// Imports necessary routines used by the runtime +import kernel.runtime.util; + +extern(C) Object _d_newclass(ClassInfo ci); + +/// Standard boolean type. +alias bool bit; + +version (X86_64) +{ + /** + * An unsigned integral type large enough to span the memory space. Use for + * array indices and pointer offsets for maximal portability to + * architectures that have different memory address ranges. This is + * analogous to C's size_t. + */ + alias ulong size_t; + + /** + * A signed integral type large enough to span the memory space. Use for + * pointer differences and for size_t differences for maximal portability to + * architectures that have different memory address ranges. This is + * analogous to C's ptrdiff_t. + */ + alias long ptrdiff_t; + + alias ulong hash_t; +} +else +{ + alias uint size_t; + alias int ptrdiff_t; + alias uint hash_t; +} + +/* ************************* + * Internal struct pointed to by the hidden .monitor member. + */ +struct Monitor +{ + void delegate(Object)[] delegates; + + /* More stuff goes here defined by internal/monitor.c */ +} + +/****************** + * All D class objects inherit from Object. + */ +class Object +{ + /** + * Convert Object to a human readable string. + */ + char[] toString() + { + return this.classinfo.name; + } + + /** + * Compute hash function for Object. + */ + hash_t toHash() + { + // BUG: this prevents a compacting GC from working, needs to be fixed + return cast(uint)cast(void *)this; + } + + /** + * Compare with another Object obj. + * Returns: + * $(TABLE + * $(TR $(TD this < obj) $(TD < 0)) + * $(TR $(TD this == obj) $(TD 0)) + * $(TR $(TD this > obj) $(TD > 0)) + * ) + */ + int opCmp(Object o) + { + // BUG: this prevents a compacting GC from working, needs to be fixed + //return cast(int)cast(void *)this - cast(int)cast(void *)o; + + throw new Error("need opCmp for class " ~ this.classinfo.name); + } + + /** + * Returns !=0 if this object does have the same contents as obj. + */ + int opEquals(Object o) + { + return cast(int)(this is o); + } + + /* ** + * Call delegate dg, passing this to it, when this object gets destroyed. + * Use extreme caution, as the list of delegates is stored in a place + * not known to the gc. Thus, if any objects pointed to by one of these + * delegates gets freed by the gc, calling the delegate will cause a + * crash. + * This is only for use by library developers, as it will need to be + * redone if weak pointers are added or a moving gc is developed. + */ + /*final void notifyRegister(void delegate(Object) dg) + { + //printf("notifyRegister(dg = %llx, o = %p)\n", dg, this); + synchronized (this) + { + Monitor* m = cast(Monitor*)(cast(void**)this)[1]; + foreach (inout x; m.delegates) + { + if (!x || x == dg) + { x = dg; + return; + } + } + + // Increase size of delegates[] + auto len = m.delegates.length; + auto startlen = len; + if (len == 0) + { + len = 4; + auto p = calloc((void delegate(Object)).sizeof, len); + if (!p) + _d_OutOfMemory(); + m.delegates = (cast(void delegate(Object)*)p)[0 .. len]; + } + else + { + len += len + 4; + auto p = realloc(m.delegates.ptr, (void delegate(Object)).sizeof * len); + if (!p) + _d_OutOfMemory(); + m.delegates = (cast(void delegate(Object)*)p)[0 .. len]; + m.delegates[startlen .. len] = null; + } + m.delegates[startlen] = dg; + } + }*/ + + /* ** + * Remove delegate dg from the notify list. + * This is only for use by library developers, as it will need to be + * redone if weak pointers are added or a moving gc is developed. + */ + /*final void notifyUnRegister(void delegate(Object) dg) + { + synchronized (this) + { + Monitor* m = cast(Monitor*)(cast(void**)this)[1]; + foreach (inout x; m.delegates) + { + if (x == dg) + x = null; + } + } + }*/ + + /****** + * Create instance of class specified by classname. + * The class must either have no constructors or have + * a default constructor. + * Returns: + * null if failed + */ + /*static Object factory(char[] classname) + { + auto ci = ClassInfo.find(classname); + if (ci) + { + return ci.create(); + } + return null; + }*/ +} + +/* +extern (C) void _d_notify_release(Object o) +{ + //printf("_d_notify_release(o = %p)\n", o); + Monitor* m = cast(Monitor*)(cast(void**)o)[1]; + if (m.delegates.length) + { + auto dgs = m.delegates; + synchronized (o) + { + dgs = m.delegates; + m.delegates = null; + } + + foreach (dg; dgs) + { + if (dg) + { //printf("calling dg = %llx (%p)\n", dg, o); + dg(o); + } + } + + free(dgs.ptr); + } +} +*/ + +/** + * Information about an interface. + * A pointer to this appears as the first entry in the interface's vtbl[]. + */ +struct Interface +{ + ClassInfo classinfo; /// .classinfo for this interface (not for containing class) + void *[] vtbl; + int offset; /// offset to Interface 'this' from Object 'this' +} + +//import std.moduleinit; +/** + * Runtime type information about a class. Can be retrieved for any class type + * or instance by using the .classinfo property. + * A pointer to this appears as the first entry in the class's vtbl[]. + */ +class ClassInfo : Object +{ + byte[] init; /** class static initializer + * (init.length gives size in bytes of class) + */ + char[] name; /// class name + void *[] vtbl; /// virtual function pointer table + Interface[] interfaces; /// interfaces this class implements + ClassInfo base; /// base class + void *destructor; + void (*classInvariant)(Object); + uint flags; + // 1: // IUnknown + // 2: // has no possible pointers into GC memory + // 4: // has offTi[] member + // 8: // has constructors + void *deallocator; + OffsetTypeInfo[] offTi; + void function(Object) defaultConstructor; // default Constructor + + /************* + * Search all modules for ClassInfo corresponding to classname. + * Returns: null if not found + */ + /*static ClassInfo find(char[] classname) + { + foreach (m; ModuleInfo.modules()) + { + //writefln("module %s, %d", m.name, m.localClasses.length); + foreach (c; m.localClasses) + { + //writefln("\tclass %s", c.name); + if (c.name == classname) + return c; + } + } + return null; + }*/ + + /******************** + * Create instance of Object represented by 'this'. + */ + Object create() + { + if (flags & 8 && !defaultConstructor) + return null; + Object o = _d_newclass(this); + if (flags & 8 && defaultConstructor) + { + defaultConstructor(o); + } + return o; + } +} + +//private import std.string; + +/** + * Array of pairs giving the offset and type information for each + * member in an aggregate. + */ +struct OffsetTypeInfo +{ + size_t offset; /// Offset of member from start of object + TypeInfo ti; /// TypeInfo for this member +} + + +/** + * Runtime type information about a type. + * Can be retrieved for any type using a + * TypeidExpression. + */ +class TypeInfo +{ + hash_t toHash() + { hash_t hash; + + foreach (char c; this.toString()) + hash = hash * 9 + c; + return hash; + } + + int opCmp(Object o) + { + if (this is o) + return 0; + TypeInfo ti = cast(TypeInfo)o; + if (ti is null) + return 1; + + char[] t = this.toString(); + char[] other = this.toString(); + + typeid(typeof(this.toString())).compare(&t, &other); + } + + int opEquals(Object o) + { + /* TypeInfo instances are singletons, but duplicates can exist + * across DLL's. Therefore, comparing for a name match is + * sufficient. + */ + if (this is o) + return 1; + TypeInfo ti = cast(TypeInfo)o; + return cast(int)(ti && this.toString() == ti.toString()); + } + + /// Returns a hash of the instance of a type. + hash_t getHash(void *p) { return cast(uint)p; } + + /// Compares two instances for equality. + int equals(void *p1, void *p2) { return cast(int)(p1 == p2); } + + /// Compares two instances for <, ==, or >. + int compare(void *p1, void *p2) { return 0; } + + /// Returns size of the type. + size_t tsize() { return 0; } + + /// Swaps two instances of the type. + void swap(void *p1, void *p2) + { + size_t n = tsize(); + for (size_t i = 0; i < n; i++) + { byte t; + + t = (cast(byte *)p1)[i]; + (cast(byte *)p1)[i] = (cast(byte *)p2)[i]; + (cast(byte *)p2)[i] = t; + } + } + + /// Get TypeInfo for 'next' type, as defined by what kind of type this is, + /// null if none. + TypeInfo next() { return null; } + + /// Return default initializer, null if default initialize to 0 + void[] init() { return null; } + + /// Get flags for type: 1 means GC should scan for pointers + uint flags() { return 0; } + + /// Get type information on the contents of the type; null if not available + OffsetTypeInfo[] offTi() { return null; } +} + +class TypeInfo_Typedef : TypeInfo +{ + char[] toString() { return name; } + + int opEquals(Object o) + { TypeInfo_Typedef c; + + return cast(int) + (this is o || + ((c = cast(TypeInfo_Typedef)o) !is null && + this.name == c.name && + this.base == c.base)); + } + + hash_t getHash(void *p) { return base.getHash(p); } + int equals(void *p1, void *p2) { return base.equals(p1, p2); } + int compare(void *p1, void *p2) { return base.compare(p1, p2); } + size_t tsize() { return base.tsize(); } + void swap(void *p1, void *p2) { return base.swap(p1, p2); } + + TypeInfo next() { return base.next(); } + uint flags() { return base.flags(); } + void[] init() { return m_init.length ? m_init : base.init(); } + + TypeInfo base; + char[] name; + void[] m_init; +} + +class TypeInfo_Enum : TypeInfo_Typedef +{ +} + +class TypeInfo_Pointer : TypeInfo +{ + char[] toString() { return m_next.toString() ~ "*"; } + + int opEquals(Object o) + { TypeInfo_Pointer c; + + return this is o || + ((c = cast(TypeInfo_Pointer)o) !is null && + this.m_next == c.m_next); + } + + hash_t getHash(void *p) + { + return cast(uint)*cast(void* *)p; + } + + int equals(void *p1, void *p2) + { + return cast(int)(*cast(void* *)p1 == *cast(void* *)p2); + } + + int compare(void *p1, void *p2) + { + if (*cast(void* *)p1 < *cast(void* *)p2) + return -1; + else if (*cast(void* *)p1 > *cast(void* *)p2) + return 1; + else + return 0; + } + + size_t tsize() + { + return (void*).sizeof; + } + + void swap(void *p1, void *p2) + { void* tmp; + tmp = *cast(void**)p1; + *cast(void**)p1 = *cast(void**)p2; + *cast(void**)p2 = tmp; + } + + TypeInfo next() { return m_next; } + uint flags() { return 1; } + + TypeInfo m_next; +} + +class TypeInfo_Array : TypeInfo +{ + char[] toString() { return value.toString() ~ "[]"; } + + int opEquals(Object o) + { TypeInfo_Array c; + + return cast(int) + (this is o || + ((c = cast(TypeInfo_Array)o) !is null && + this.value == c.value)); + } + + hash_t getHash(void *p) + { size_t sz = value.tsize(); + hash_t hash = 0; + void[] a = *cast(void[]*)p; + for (size_t i = 0; i < a.length; i++) + hash += value.getHash(a.ptr + i * sz); + return hash; + } + + int equals(void *p1, void *p2) + { + void[] a1 = *cast(void[]*)p1; + void[] a2 = *cast(void[]*)p2; + if (a1.length != a2.length) + return 0; + size_t sz = value.tsize(); + for (size_t i = 0; i < a1.length; i++) + { + if (!value.equals(a1.ptr + i * sz, a2.ptr + i * sz)) + return 0; + } + return 1; + } + + int compare(void *p1, void *p2) + { + void[] a1 = *cast(void[]*)p1; + void[] a2 = *cast(void[]*)p2; + size_t sz = value.tsize(); + size_t len = a1.length; + + if (a2.length < len) + len = a2.length; + for (size_t u = 0; u < len; u++) + { + int result = value.compare(a1.ptr + u * sz, a2.ptr + u * sz); + if (result) + return result; + } + return cast(int)a1.length - cast(int)a2.length; + } + + size_t tsize() + { + return (void[]).sizeof; + } + + void swap(void *p1, void *p2) + { void[] tmp; + tmp = *cast(void[]*)p1; + *cast(void[]*)p1 = *cast(void[]*)p2; + *cast(void[]*)p2 = tmp; + } + + TypeInfo value; + + TypeInfo next() + { + return value; + } + + uint flags() { return 1; } +} + +class TypeInfo_StaticArray : TypeInfo +{ + char[] toString() + { + char[20] buf; + return value.toString() ~ "[" ~ itoa(buf, 'd', len) ~ "]"; + } + + int opEquals(Object o) + { TypeInfo_StaticArray c; + + return cast(int) + (this is o || + ((c = cast(TypeInfo_StaticArray)o) !is null && + this.len == c.len && + this.value == c.value)); + } + + hash_t getHash(void *p) + { size_t sz = value.tsize(); + hash_t hash = 0; + for (size_t i = 0; i < len; i++) + hash += value.getHash(p + i * sz); + return hash; + } + + int equals(void *p1, void *p2) + { + size_t sz = value.tsize(); + + for (size_t u = 0; u < len; u++) + { + if (!value.equals(p1 + u * sz, p2 + u * sz)) + return 0; + } + return 1; + } + + int compare(void *p1, void *p2) + { + size_t sz = value.tsize(); + + for (size_t u = 0; u < len; u++) + { + int result = value.compare(p1 + u * sz, p2 + u * sz); + if (result) + return result; + } + return 0; + } + + size_t tsize() + { + return len * value.tsize(); + } + + void swap(void *p1, void *p2) + { void* tmp; + size_t sz = value.tsize(); + ubyte[16] buffer; + void* pbuffer; + + if (sz < buffer.sizeof) + tmp = buffer.ptr; + else + tmp = pbuffer = (new void[sz]).ptr; + + for (size_t u = 0; u < len; u += sz) + { size_t o = u * sz; + tmp[0 .. sz] = (p1 + o)[0 .. sz]; + (p1 + o)[0 .. sz] = (p2 + o)[0 .. sz]; + (p2 + o)[0 .. sz] = tmp[0 .. sz]; + } + if (pbuffer) + delete pbuffer; + } + + void[] init() { return value.init(); } + TypeInfo next() { return value; } + uint flags() { return value.flags(); } + + TypeInfo value; + size_t len; +} + +class TypeInfo_AssociativeArray : TypeInfo +{ + char[] toString() + { + return value.toString() ~ "[" ~ key.toString() ~ "]"; + } + + int opEquals(Object o) + { TypeInfo_AssociativeArray c; + + return this is o || + ((c = cast(TypeInfo_AssociativeArray)o) !is null && + this.key == c.key && + this.value == c.value); + } + + // BUG: need to add the rest of the functions + + size_t tsize() + { + return (char[int]).sizeof; + } + + TypeInfo next() { return value; } + uint flags() { return 1; } + + TypeInfo value; + TypeInfo key; +} + +class TypeInfo_Function : TypeInfo +{ + char[] toString() + { + return next.toString() ~ "()"; + } + + int opEquals(Object o) + { TypeInfo_Function c; + + return this is o || + ((c = cast(TypeInfo_Function)o) !is null && + this.next == c.next); + } + + // BUG: need to add the rest of the functions + + size_t tsize() + { + return 0; // no size for functions + } + + TypeInfo next; +} + +class TypeInfo_Delegate : TypeInfo +{ + char[] toString() + { + return next.toString() ~ " delegate()"; + } + + int opEquals(Object o) + { TypeInfo_Delegate c; + + return this is o || + ((c = cast(TypeInfo_Delegate)o) !is null && + this.next == c.next); + } + + // BUG: need to add the rest of the functions + + size_t tsize() + { alias int delegate() dg; + return dg.sizeof; + } + + uint flags() { return 1; } + + TypeInfo next; +} + +class TypeInfo_Class : TypeInfo +{ + char[] toString() { return info.name; } + + int opEquals(Object o) + { TypeInfo_Class c; + + return this is o || + ((c = cast(TypeInfo_Class)o) !is null && + this.info.name == c.classinfo.name); + } + + hash_t getHash(void *p) + { + Object o = *cast(Object*)p; + assert(o); + return o.toHash(); + } + + int equals(void *p1, void *p2) + { + Object o1 = *cast(Object*)p1; + Object o2 = *cast(Object*)p2; + + return (o1 is o2) || (o1 && o1.opEquals(o2)); + } + + int compare(void *p1, void *p2) + { + Object o1 = *cast(Object*)p1; + Object o2 = *cast(Object*)p2; + int c = 0; + + // Regard null references as always being "less than" + if (o1 !is o2) + { + if (o1) + { if (!o2) + c = 1; + else + c = o1.opCmp(o2); + } + else + c = -1; + } + return c; + } + + size_t tsize() + { + return Object.sizeof; + } + + uint flags() { return 1; } + + OffsetTypeInfo[] offTi() + { + return (info.flags & 4) ? info.offTi : null; + } + + ClassInfo info; +} + +class TypeInfo_Interface : TypeInfo +{ + char[] toString() { return info.name; } + + int opEquals(Object o) + { TypeInfo_Interface c; + + return this is o || + ((c = cast(TypeInfo_Interface)o) !is null && + this.info.name == c.classinfo.name); + } + + hash_t getHash(void *p) + { + Interface* pi = **cast(Interface ***)*cast(void**)p; + Object o = cast(Object)(*cast(void**)p - pi.offset); + assert(o); + return o.toHash(); + } + + int equals(void *p1, void *p2) + { + Interface* pi = **cast(Interface ***)*cast(void**)p1; + Object o1 = cast(Object)(*cast(void**)p1 - pi.offset); + pi = **cast(Interface ***)*cast(void**)p2; + Object o2 = cast(Object)(*cast(void**)p2 - pi.offset); + + return o1 == o2 || (o1 && o1.opCmp(o2) == 0); + } + + int compare(void *p1, void *p2) + { + Interface* pi = **cast(Interface ***)*cast(void**)p1; + Object o1 = cast(Object)(*cast(void**)p1 - pi.offset); + pi = **cast(Interface ***)*cast(void**)p2; + Object o2 = cast(Object)(*cast(void**)p2 - pi.offset); + int c = 0; + + // Regard null references as always being "less than" + if (o1 != o2) + { + if (o1) + { if (!o2) + c = 1; + else + c = o1.opCmp(o2); + } + else + c = -1; + } + return c; + } + + size_t tsize() + { + return Object.sizeof; + } + + uint flags() { return 1; } + + ClassInfo info; +} + +class TypeInfo_Struct : TypeInfo +{ + char[] toString() { return name; } + + int opEquals(Object o) + { TypeInfo_Struct s; + + return this is o || + ((s = cast(TypeInfo_Struct)o) !is null && + this.name == s.name && + this.init.length == s.init.length); + } + + hash_t getHash(void *p) + { hash_t h; + + assert(p); + if (xtoHash) + { //printf("getHash() using xtoHash\n"); + h = (*xtoHash)(p); + } + else + { + //printf("getHash() using default hash\n"); + // A sorry hash algorithm. + // Should use the one for strings. + // BUG: relies on the GC not moving objects + for (size_t i = 0; i < init.length; i++) + { h = h * 9 + *cast(ubyte*)p; + p++; + } + } + return h; + } + + int equals(void *p2, void *p1) + { int c; + + if (p1 == p2) + c = 1; + else if (!p1 || !p2) + c = 0; + else if (xopEquals) + c = (*xopEquals)(p1, p2); + else + // BUG: relies on the GC not moving objects + c = (memcmp(cast(ubyte*)p1, cast(ubyte*)p2, init.length) == 0); + return c; + } + + int compare(void *p2, void *p1) + { + int c = 0; + + // Regard null references as always being "less than" + if (p1 != p2) + { + if (p1) + { if (!p2) + c = 1; + else if (xopCmp) + c = (*xopCmp)(p1, p2); + else + // BUG: relies on the GC not moving objects + c = memcmp(cast(ubyte*)p1, cast(ubyte*)p2, init.length); + } + else + c = -1; + } + return c; + } + + size_t tsize() + { + return init.length; + } + + void[] init() { return m_init; } + + uint flags() { return m_flags; } + + char[] name; + void[] m_init; // initializer; init.ptr == null if 0 initialize + + hash_t function(void*) xtoHash; + int function(void*,void*) xopEquals; + int function(void*,void*) xopCmp; + char[] function(void*) xtoString; + + uint m_flags; +} + +class TypeInfo_Tuple : TypeInfo +{ + TypeInfo[] elements; + + char[] toString() + { + char[] s; + s = "("; + foreach (i, element; elements) + { + if (i) + s ~= ','; + s ~= element.toString(); + } + s ~= ")"; + return s; + } + + int opEquals(Object o) + { + if (this is o) + return 1; + + auto t = cast(TypeInfo_Tuple)o; + if (t && elements.length == t.elements.length) + { + for (size_t i = 0; i < elements.length; i++) + { + if (elements[i] != t.elements[i]) + return 0; + } + return 1; + } + return 0; + } + + hash_t getHash(void *p) + { + assert(0); + } + + int equals(void *p1, void *p2) + { + assert(0); + } + + int compare(void *p1, void *p2) + { + assert(0); + } + + size_t tsize() + { + assert(0); + } + + void swap(void *p1, void *p2) + { + assert(0); + } +} + +/** + * All recoverable exceptions should be derived from class Exception. + */ +class Exception : Object +{ + char[] msg; + + /** + * Constructor; msg is a descriptive message for the exception. + */ + this(char[] msg) + { + this.msg = msg; + } + + char[] toString() { return msg; } +} + +/** + * All irrecoverable exceptions should be derived from class Error. + */ +class Error : Exception +{ + Error next; + + /** + * Constructor; msg is a descriptive message for the exception. + */ + this(char[] msg) + { + super(msg); + } + + this(char[] msg, Error next) + { + super(msg); + this.next = next; + } +} + +//extern (C) int nullext = 0; + diff --git a/kernel/runtime/std/c/stdarg.d b/kernel/runtime/std/c/stdarg.d new file mode 100644 index 0000000..8c05001 --- /dev/null +++ b/kernel/runtime/std/c/stdarg.d @@ -0,0 +1,34 @@ + +/* + * Placed in public domain. + * Written by Hauke Duden and Walter Bright + */ + +/* This is for use with variable argument lists with extern(D) linkage. */ + +module std.c.stdarg; + +private import gcc.builtins; +alias __builtin_va_list va_list; +alias __builtin_va_end va_end; +alias __builtin_va_copy va_copy; + +// The va_start and va_arg template functions are magically +// handled by the compiler. + +template va_start(T) +{ + void va_start(out va_list ap, inout T parmn) + { + + } +} + +template va_arg(T) +{ + T va_arg(ref va_list _argptr) + { + return T.init; + } +} + diff --git a/kernel/runtime/std/intrinsic.d b/kernel/runtime/std/intrinsic.d new file mode 100644 index 0000000..4a62f41 --- /dev/null +++ b/kernel/runtime/std/intrinsic.d @@ -0,0 +1,264 @@ + + +// written by Walter Bright +// www.digitalmars.com +// Placed into the public domain + +/* NOTE: This file has been patched from the original DMD distribution to + work with the GDC compiler. + + Modified by David Friedman, May 2006 +*/ + +/** These functions are built-in intrinsics to the compiler. + * + Intrinsic functions are functions built in to the compiler, + usually to take advantage of specific CPU features that + are inefficient to handle via external functions. + The compiler's optimizer and code generator are fully + integrated in with intrinsic functions, bringing to bear + their full power on them. + This can result in some surprising speedups. + * Macros: + * WIKI=Phobos/StdIntrinsic + */ + +module std.intrinsic; + +/** + * Scans the bits in v starting with bit 0, looking + * for the first set bit. + * Returns: + * The bit number of the first bit set. + * The return value is undefined if v is zero. + */ +version (GNU) + int bsf(uint v) + { + uint m = 1; + uint i; + for (i = 0; i < 32; i++,m<<=1) { + if (v&m) + return i; + } + return i; // supposed to be undefined + } +else + int bsf(uint v); + +/** + * Scans the bits in v from the most significant bit + * to the least significant bit, looking + * for the first set bit. + * Returns: + * The bit number of the first bit set. + * The return value is undefined if v is zero. + * Example: + * --- + * import std.intrinsic; + * + * int main() + * { + * uint v; + * int x; + * + * v = 0x21; + * x = bsf(v); + * printf("bsf(x%x) = %d\n", v, x); + * x = bsr(v); + * printf("bsr(x%x) = %d\n", v, x); + * return 0; + * } + * --- + * Output: + * bsf(x21) = 0
+ * bsr(x21) = 5 + */ +version (GNU) +int bsr(uint v) +{ + uint m = 0x80000000; + uint i; + for (i = 32; i ; i--,m>>>=1) { + if (v&m) + return i-1; + } + return i; // supposed to be undefined +} +else + int bsr(uint v); + +/** + * Tests the bit. + */ +version (GNU) +int bt(uint *p, uint bitnum) +{ + return (p[bitnum / (uint.sizeof*8)] & (1<<(bitnum & ((uint.sizeof*8)-1)))) ? -1 : 0 ; +} +else + int bt(uint *p, uint bitnum); + +/** + * Tests and complements the bit. + */ +version (GNU) +int btc(uint *p, uint bitnum) +{ + uint * q = p + (bitnum / (uint.sizeof*8)); + uint mask = 1 << (bitnum & ((uint.sizeof*8) - 1)); + int result = *q & mask; + *q ^= mask; + return result ? -1 : 0; +} +else +int btc(uint *p, uint bitnum); + +/** + * Tests and resets (sets to 0) the bit. + */ +version (GNU) +int btr(uint *p, uint bitnum) +{ + uint * q = p + (bitnum / (uint.sizeof*8)); + uint mask = 1 << (bitnum & ((uint.sizeof*8) - 1)); + int result = *q & mask; + *q &= ~mask; + return result ? -1 : 0; +} +else + int btr(uint *p, uint bitnum); + +/** + * Tests and sets the bit. + * Params: + * p = a non-NULL pointer to an array of uints. + * index = a bit number, starting with bit 0 of p[0], + * and progressing. It addresses bits like the expression: +--- +p[index / (uint.sizeof*8)] & (1 << (index & ((uint.sizeof*8) - 1))) +--- + * Returns: + * A non-zero value if the bit was set, and a zero + * if it was clear. + * + * Example: + * --- +import std.intrinsic; + +int main() +{ + uint array[2]; + + array[0] = 2; + array[1] = 0x100; + + printf("btc(array, 35) = %d\n", btc(array, 35)); + printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]); + + printf("btc(array, 35) = %d\n", btc(array, 35)); + printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]); + + printf("bts(array, 35) = %d\n", bts(array, 35)); + printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]); + + printf("btr(array, 35) = %d\n", btr(array, 35)); + printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]); + + printf("bt(array, 1) = %d\n", bt(array, 1)); + printf("array = [0]:x%x, [1]:x%x\n", array[0], array[1]); + + return 0; +} + * --- + * Output: +

+btc(array, 35) = 0
+array = [0]:x2, [1]:x108
+btc(array, 35) = -1
+array = [0]:x2, [1]:x100
+bts(array, 35) = 0
+array = [0]:x2, [1]:x108
+btr(array, 35) = -1
+array = [0]:x2, [1]:x100
+bt(array, 1) = -1
+array = [0]:x2, [1]:x100
+
+ */ +version (GNU) +int bts(uint *p, uint bitnum) +{ + uint * q = p + (bitnum / (uint.sizeof*8)); + uint mask = 1 << (bitnum & ((uint.sizeof*8) - 1)); + int result = *q & mask; + *q |= mask; + return result ? -1 : 0; +} +else + int bts(uint *p, uint bitnum); + + +/** + * Swaps bytes in a 4 byte uint end-to-end, i.e. byte 0 becomes + byte 3, byte 1 becomes byte 2, byte 2 becomes byte 1, byte 3 + becomes byte 0. + */ +version (GNU) +uint bswap(uint v) +{ + return ((v&0xFF)<<24)|((v&0xFF00)<<8)|((v&0xFF0000)>>>8)|((v&0xFF000000)>>>24); +} +else + uint bswap(uint v); + + +/** + * Reads I/O port at port_address. + */ +version (GNU) + ubyte inp(uint p) { return 0; } +else + ubyte inp(uint port_address); + +/** + * ditto + */ +version (GNU) + ushort inpw(uint p) { return 0; } +else + ushort inpw(uint port_address); + +/** + * ditto + */ +version (GNU) + uint inpl(uint p) { return 0; } +else + uint inpl(uint port_address); + + +/** + * Writes and returns value to I/O port at port_address. + */ +version (GNU) + ubyte outp(uint p, ubyte v) { return v; } +else + ubyte outp(uint port_address, ubyte value); + +/** + * ditto + */ +version (GNU) + ushort outpw(uint p, ushort v) { return v; } +else + ushort outpw(uint port_address, ushort value); + +/** + * ditto + */ +version (GNU) + uint outpl(uint p, uint v) { return v; } +else + uint outpl(uint port_address, uint value); + + diff --git a/kernel/runtime/std/stdarg.d b/kernel/runtime/std/stdarg.d new file mode 100644 index 0000000..6bac4e5 --- /dev/null +++ b/kernel/runtime/std/stdarg.d @@ -0,0 +1,43 @@ + +/* + * Placed in public domain. + * Written by Hauke Duden and Walter Bright + */ + +/* This is for use with variable argument lists with extern(D) linkage. */ + +/* NOTE: This file has been patched from the original DMD distribution to + work with the GDC compiler. + + Modified by David Friedman, September 2004 +*/ + +module std.stdarg; + +version (GNU) { + // va_list might be a pointer, but assuming so is not portable. + private import gcc.builtins; + alias __builtin_va_list va_list; + + // va_arg is handled magically by the compiler +} else { + alias void* va_list; +} + +template va_arg(T) +{ + T va_arg(inout va_list _argptr) + { + /* + T arg = *cast(T*)_argptr; + _argptr = _argptr + ((T.sizeof + int.sizeof - 1) & ~(int.sizeof - 1)); + return arg; + */ + T t; return t; + } +} + +private import std.c.stdarg; +/* The existence of std.stdarg.va_copy isn't standard. Prevent + conflicts by using '__'. */ +alias std.c.stdarg.va_copy __va_copy; diff --git a/kernel/runtime/std/typeinfo/ti_AC.d b/kernel/runtime/std/typeinfo/ti_AC.d new file mode 100644 index 0000000..aa49f27 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_AC.d @@ -0,0 +1,96 @@ +module std.typeinfo.ti_AC; + +// Object[] + +class TypeInfo_AC : TypeInfo +{ + hash_t getHash(void *p) + { Object[] s = *cast(Object[]*)p; + hash_t hash = 0; + + foreach (Object o; s) + { + if (o) + hash += o.toHash(); + } + return hash; + } + + int equals(void *p1, void *p2) + { + Object[] s1 = *cast(Object[]*)p1; + Object[] s2 = *cast(Object[]*)p2; + + if (s1.length == s2.length) + { + for (size_t u = 0; u < s1.length; u++) + { Object o1 = s1[u]; + Object o2 = s2[u]; + + // Do not pass null's to Object.opEquals() + if (o1 is o2 || + (!(o1 is null) && !(o2 is null) && o1.opEquals(o2))) + continue; + return 0; + } + return 1; + } + return 0; + } + + int compare(void *p1, void *p2) + { + Object[] s1 = *cast(Object[]*)p1; + Object[] s2 = *cast(Object[]*)p2; + ptrdiff_t c; + + c = cast(ptrdiff_t)s1.length - cast(ptrdiff_t)s2.length; + if (c == 0) + { + for (size_t u = 0; u < s1.length; u++) + { Object o1 = s1[u]; + Object o2 = s2[u]; + + if (o1 is o2) + continue; + + // Regard null references as always being "less than" + if (o1) + { + if (!o2) + { c = 1; + break; + } + c = o1.opCmp(o2); + if (c) + break; + } + else + { c = -1; + break; + } + } + } + if (c < 0) + c = -1; + else if (c > 0) + c = 1; + return c; + } + + size_t tsize() + { + return (Object[]).sizeof; + } + + uint flags() + { + return 1; + } + + TypeInfo next() + { + return typeid(Object); + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_Acdouble.d b/kernel/runtime/std/typeinfo/ti_Acdouble.d new file mode 100644 index 0000000..6057d63 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_Acdouble.d @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2004-2005 by Digital Mars, www.digitalmars.com + * Written by Walter Bright + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, in both source and binary form, subject to the following + * restrictions: + * + * o The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * o Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * o This notice may not be removed or altered from any source + * distribution. + */ + +module std.typeinfo.ti_Acdouble; + +private import std.typeinfo.ti_cdouble; + +// cdouble[] + +class TypeInfo_Ar : TypeInfo +{ + char[] toString() { return "cdouble[]"; } + + hash_t getHash(void *p) + { cdouble[] s = *cast(cdouble[]*)p; + size_t len = s.length; + cdouble *str = s.ptr; + hash_t hash = 0; + + while (len) + { + hash *= 9; + hash += (cast(uint *)str)[0]; + hash += (cast(uint *)str)[1]; + hash += (cast(uint *)str)[2]; + hash += (cast(uint *)str)[3]; + str++; + len--; + } + + return hash; + } + + int equals(void *p1, void *p2) + { + cdouble[] s1 = *cast(cdouble[]*)p1; + cdouble[] s2 = *cast(cdouble[]*)p2; + size_t len = s1.length; + + if (len != s2.length) + return 0; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_r._equals(s1[u], s2[u]); + if (c == 0) + return 0; + } + return 1; + } + + int compare(void *p1, void *p2) + { + cdouble[] s1 = *cast(cdouble[]*)p1; + cdouble[] s2 = *cast(cdouble[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_r._compare(s1[u], s2[u]); + if (c) + return c; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + size_t tsize() + { + return (cdouble[]).sizeof; + } + + uint flags() + { + return 1; + } + + TypeInfo next() + { + return typeid(cdouble); + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_Acfloat.d b/kernel/runtime/std/typeinfo/ti_Acfloat.d new file mode 100644 index 0000000..88639f4 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_Acfloat.d @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2004-2005 by Digital Mars, www.digitalmars.com + * Written by Walter Bright + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, in both source and binary form, subject to the following + * restrictions: + * + * o The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * o Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * o This notice may not be removed or altered from any source + * distribution. + */ + +module std.typeinfo.ti_Acfloat; + +private import std.typeinfo.ti_cfloat; + +// cfloat[] + +class TypeInfo_Aq : TypeInfo +{ + char[] toString() { return "cfloat[]"; } + + hash_t getHash(void *p) + { cfloat[] s = *cast(cfloat[]*)p; + size_t len = s.length; + cfloat *str = s.ptr; + hash_t hash = 0; + + while (len) + { + hash *= 9; + hash += (cast(uint *)str)[0]; + hash += (cast(uint *)str)[1]; + str++; + len--; + } + + return hash; + } + + int equals(void *p1, void *p2) + { + cfloat[] s1 = *cast(cfloat[]*)p1; + cfloat[] s2 = *cast(cfloat[]*)p2; + size_t len = s1.length; + + if (len != s2.length) + return 0; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_q._equals(s1[u], s2[u]); + if (c == 0) + return 0; + } + return 1; + } + + int compare(void *p1, void *p2) + { + cfloat[] s1 = *cast(cfloat[]*)p1; + cfloat[] s2 = *cast(cfloat[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_q._compare(s1[u], s2[u]); + if (c) + return c; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + size_t tsize() + { + return (cfloat[]).sizeof; + } + + uint flags() + { + return 1; + } + + TypeInfo next() + { + return typeid(cfloat); + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_Acreal.d b/kernel/runtime/std/typeinfo/ti_Acreal.d new file mode 100644 index 0000000..0514c9e --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_Acreal.d @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2004-2005 by Digital Mars, www.digitalmars.com + * Written by Walter Bright + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, in both source and binary form, subject to the following + * restrictions: + * + * o The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * o Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * o This notice may not be removed or altered from any source + * distribution. + */ + +module std.typeinfo.ti_Acreal; + +private import std.typeinfo.ti_creal; + +// creal[] + +class TypeInfo_Ac : TypeInfo +{ + char[] toString() { return "creal[]"; } + + hash_t getHash(void *p) + { creal[] s = *cast(creal[]*)p; + size_t len = s.length; + creal *str = s.ptr; + hash_t hash = 0; + + while (len) + { + hash *= 9; + hash += (cast(uint *)str)[0]; + hash += (cast(uint *)str)[1]; + hash += (cast(uint *)str)[2]; + hash += (cast(uint *)str)[3]; + hash += (cast(uint *)str)[4]; + str++; + len--; + } + + return hash; + } + + int equals(void *p1, void *p2) + { + creal[] s1 = *cast(creal[]*)p1; + creal[] s2 = *cast(creal[]*)p2; + size_t len = s1.length; + + if (len != s2.length) + return 0; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_c._equals(s1[u], s2[u]); + if (c == 0) + return 0; + } + return 1; + } + + int compare(void *p1, void *p2) + { + creal[] s1 = *cast(creal[]*)p1; + creal[] s2 = *cast(creal[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_c._compare(s1[u], s2[u]); + if (c) + return c; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + size_t tsize() + { + return (creal[]).sizeof; + } + + uint flags() + { + return 1; + } + + TypeInfo next() + { + return typeid(creal); + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_Adouble.d b/kernel/runtime/std/typeinfo/ti_Adouble.d new file mode 100644 index 0000000..c348913 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_Adouble.d @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2004-2005 by Digital Mars, www.digitalmars.com + * Written by Walter Bright + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, in both source and binary form, subject to the following + * restrictions: + * + * o The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * o Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * o This notice may not be removed or altered from any source + * distribution. + */ + +module std.typeinfo.ti_Adouble; + +private import std.typeinfo.ti_double; + +// double[] + +class TypeInfo_Ad : TypeInfo +{ + char[] toString() { return "double[]"; } + + hash_t getHash(void *p) + { double[] s = *cast(double[]*)p; + size_t len = s.length; + auto str = s.ptr; + hash_t hash = 0; + + while (len) + { + hash *= 9; + hash += (cast(uint *)str)[0]; + hash += (cast(uint *)str)[1]; + str++; + len--; + } + + return hash; + } + + int equals(void *p1, void *p2) + { + double[] s1 = *cast(double[]*)p1; + double[] s2 = *cast(double[]*)p2; + size_t len = s1.length; + + if (len != s2.length) + return 0; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_d._equals(s1[u], s2[u]); + if (c == 0) + return 0; + } + return 1; + } + + int compare(void *p1, void *p2) + { + double[] s1 = *cast(double[]*)p1; + double[] s2 = *cast(double[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_d._compare(s1[u], s2[u]); + if (c) + return c; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + size_t tsize() + { + return (double[]).sizeof; + } + + uint flags() + { + return 1; + } + + TypeInfo next() + { + return typeid(double); + } +} + +// idouble[] + +class TypeInfo_Ap : TypeInfo_Ad +{ + char[] toString() { return "idouble[]"; } + + TypeInfo next() + { + return typeid(idouble); + } +} diff --git a/kernel/runtime/std/typeinfo/ti_Afloat.d b/kernel/runtime/std/typeinfo/ti_Afloat.d new file mode 100644 index 0000000..fa8bef5 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_Afloat.d @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2004-2005 by Digital Mars, www.digitalmars.com + * Written by Walter Bright + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, in both source and binary form, subject to the following + * restrictions: + * + * o The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * o Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * o This notice may not be removed or altered from any source + * distribution. + */ + +module std.typeinfo.ti_Afloat; + +private import std.typeinfo.ti_float; + +// float[] + +class TypeInfo_Af : TypeInfo +{ + char[] toString() { return "float[]"; } + + hash_t getHash(void *p) + { float[] s = *cast(float[]*)p; + size_t len = s.length; + auto str = s.ptr; + hash_t hash = 0; + + while (len) + { + hash *= 9; + hash += *cast(uint *)str; + str++; + len--; + } + + return hash; + } + + int equals(void *p1, void *p2) + { + float[] s1 = *cast(float[]*)p1; + float[] s2 = *cast(float[]*)p2; + size_t len = s1.length; + + if (len != s2.length) + return 0; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_f._equals(s1[u], s2[u]); + if (c == 0) + return 0; + } + return 1; + } + + int compare(void *p1, void *p2) + { + float[] s1 = *cast(float[]*)p1; + float[] s2 = *cast(float[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_f._compare(s1[u], s2[u]); + if (c) + return c; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + size_t tsize() + { + return (float[]).sizeof; + } + + uint flags() + { + return 1; + } + + TypeInfo next() + { + return typeid(float); + } +} + +// ifloat[] + +class TypeInfo_Ao : TypeInfo_Af +{ + char[] toString() { return "ifloat[]"; } + + TypeInfo next() + { + return typeid(ifloat); + } +} diff --git a/kernel/runtime/std/typeinfo/ti_Ag.d b/kernel/runtime/std/typeinfo/ti_Ag.d new file mode 100644 index 0000000..eb94d30 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_Ag.d @@ -0,0 +1,204 @@ + +module std.typeinfo.ti_Ag; +import kernel.runtime.util; + +// byte[] + +class TypeInfo_Ag : TypeInfo +{ + char[] toString() { return "byte[]"; } + + hash_t getHash(void *p) + { byte[] s = *cast(byte[]*)p; + size_t len = s.length; + byte *str = s.ptr; + hash_t hash = 0; + + while (1) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash *= 9; + hash += *cast(ubyte *)str; + return hash; + + case 2: + hash *= 9; + hash += *cast(ushort *)str; + return hash; + + case 3: + hash *= 9; + hash += (*cast(ushort *)str << 8) + + (cast(ubyte *)str)[2]; + return hash; + + default: + hash *= 9; + hash += *cast(uint *)str; + str += 4; + len -= 4; + break; + } + } + + return hash; + } + + int equals(void *p1, void *p2) + { + ubyte[] s1 = *cast(ubyte[]*)p1; + ubyte[] s2 = *cast(ubyte[]*)p2; + + return s1.length == s2.length && + memcmp(s1.ptr, s2.ptr, s1.length) == 0; + } + + int compare(void *p1, void *p2) + { + byte[] s1 = *cast(byte[]*)p1; + byte[] s2 = *cast(byte[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + int result = s1[u] - s2[u]; + if (result) + return result; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + size_t tsize() + { + return (byte[]).sizeof; + } + + uint flags() + { + return 1; + } + + TypeInfo next() + { + return typeid(byte); + } +} + + +// ubyte[] + +class TypeInfo_Ah : TypeInfo_Ag +{ + char[] toString() { return "ubyte[]"; } + + int compare(void *p1, void *p2) + { + ubyte[] s1 = *cast(ubyte[]*)p1; + ubyte[] s2 = *cast(ubyte[]*)p2; + + return memcmp(s1.ptr, s2.ptr, s1.length); + } + + TypeInfo next() + { + return typeid(ubyte); + } +} + +// void[] + +class TypeInfo_Av : TypeInfo_Ah +{ + char[] toString() { return "void[]"; } + + TypeInfo next() + { + return typeid(void); + } +} + +// bool[] + +class TypeInfo_Ab : TypeInfo_Ah +{ + char[] toString() { return "bool[]"; } + + TypeInfo next() + { + return typeid(bool); + } +} + +// char[] + +class TypeInfo_Aa : TypeInfo_Ag +{ + char[] toString() { return "char[]"; } + + hash_t getHash(void *p) + { char[] s = *cast(char[]*)p; + hash_t hash = 0; + +version (all) +{ + foreach (char c; s) + hash = hash * 11 + c; +} +else +{ + size_t len = s.length; + char *str = s; + + while (1) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash *= 9; + hash += *cast(ubyte *)str; + return hash; + + case 2: + hash *= 9; + hash += *cast(ushort *)str; + return hash; + + case 3: + hash *= 9; + hash += (*cast(ushort *)str << 8) + + (cast(ubyte *)str)[2]; + return hash; + + default: + hash *= 9; + hash += *cast(uint *)str; + str += 4; + len -= 4; + break; + } + } +} + return hash; + } + + TypeInfo next() + { + return typeid(char); + } +} + + diff --git a/kernel/runtime/std/typeinfo/ti_Aint.d b/kernel/runtime/std/typeinfo/ti_Aint.d new file mode 100644 index 0000000..8cfc2b6 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_Aint.d @@ -0,0 +1,119 @@ + +module std.typeinfo.ti_Aint; + +import kernel.runtime.util; + +// int[] + +class TypeInfo_Ai : TypeInfo +{ + char[] toString() { return "int[]"; } + + hash_t getHash(void *p) + { int[] s = *cast(int[]*)p; + auto len = s.length; + auto str = s.ptr; + hash_t hash = 0; + + while (len) + { + hash *= 9; + hash += *cast(uint *)str; + str++; + len--; + } + + return hash; + } + + int equals(void *p1, void *p2) + { + int[] s1 = *cast(int[]*)p1; + int[] s2 = *cast(int[]*)p2; + + return s1.length == s2.length && + memcmp(cast(ubyte*)s1.ptr, cast(ubyte*)s2.ptr, s1.length * int.sizeof) == 0; + } + + int compare(void *p1, void *p2) + { + int[] s1 = *cast(int[]*)p1; + int[] s2 = *cast(int[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + int result = s1[u] - s2[u]; + if (result) + return result; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + size_t tsize() + { + return (int[]).sizeof; + } + + uint flags() + { + return 1; + } + + TypeInfo next() + { + return typeid(int); + } +} + +// uint[] + +class TypeInfo_Ak : TypeInfo_Ai +{ + char[] toString() { return "uint[]"; } + + int compare(void *p1, void *p2) + { + uint[] s1 = *cast(uint[]*)p1; + uint[] s2 = *cast(uint[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + int result = s1[u] - s2[u]; + if (result) + return result; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + TypeInfo next() + { + return typeid(uint); + } +} + +// dchar[] + +class TypeInfo_Aw : TypeInfo_Ak +{ + char[] toString() { return "dchar[]"; } + + TypeInfo next() + { + return typeid(dchar); + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_Along.d b/kernel/runtime/std/typeinfo/ti_Along.d new file mode 100644 index 0000000..7fe1710 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_Along.d @@ -0,0 +1,111 @@ + +module std.typeinfo.ti_Along; + +import kernel.runtime.util; + +// long[] + +class TypeInfo_Al : TypeInfo +{ + char[] toString() { return "long[]"; } + + hash_t getHash(void *p) + { long[] s = *cast(long[]*)p; + size_t len = s.length; + auto str = s.ptr; + hash_t hash = 0; + + while (len) + { + hash *= 9; + hash += *cast(uint *)str + *(cast(uint *)str + 1); + str++; + len--; + } + + return hash; + } + + int equals(void *p1, void *p2) + { + long[] s1 = *cast(long[]*)p1; + long[] s2 = *cast(long[]*)p2; + + return s1.length == s2.length && + memcmp(cast(ubyte*)s1.ptr, cast(ubyte*)s2.ptr, s1.length * long.sizeof) == 0; + } + + int compare(void *p1, void *p2) + { + long[] s1 = *cast(long[]*)p1; + long[] s2 = *cast(long[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + if (s1[u] < s2[u]) + return -1; + else if (s1[u] > s2[u]) + return 1; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + size_t tsize() + { + return (long[]).sizeof; + } + + uint flags() + { + return 1; + } + + TypeInfo next() + { + return typeid(long); + } +} + + +// ulong[] + +class TypeInfo_Am : TypeInfo_Al +{ + char[] toString() { return "ulong[]"; } + + int compare(void *p1, void *p2) + { + ulong[] s1 = *cast(ulong[]*)p1; + ulong[] s2 = *cast(ulong[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + if (s1[u] < s2[u]) + return -1; + else if (s1[u] > s2[u]) + return 1; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + TypeInfo next() + { + return typeid(ulong); + } +} + + diff --git a/kernel/runtime/std/typeinfo/ti_Areal.d b/kernel/runtime/std/typeinfo/ti_Areal.d new file mode 100644 index 0000000..f68af32 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_Areal.d @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2004-2006 by Digital Mars, www.digitalmars.com + * Written by Walter Bright + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, in both source and binary form, subject to the following + * restrictions: + * + * o The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * o Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * o This notice may not be removed or altered from any source + * distribution. + */ + +module std.typeinfo.ti_Areal; + +private import std.typeinfo.ti_real; + +// real[] + +class TypeInfo_Ae : TypeInfo +{ + char[] toString() { return "real[]"; } + + hash_t getHash(void *p) + { real[] s = *cast(real[]*)p; + size_t len = s.length; + auto str = s.ptr; + hash_t hash = 0; + + while (len) + { + hash *= 9; + hash += (cast(uint *)str)[0]; + hash += (cast(uint *)str)[1]; + hash += (cast(ushort *)str)[4]; + str++; + len--; + } + + return hash; + } + + int equals(void *p1, void *p2) + { + real[] s1 = *cast(real[]*)p1; + real[] s2 = *cast(real[]*)p2; + size_t len = s1.length; + + if (len != s2.length) + return 0; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_e._equals(s1[u], s2[u]); + if (c == 0) + return 0; + } + return 1; + } + + int compare(void *p1, void *p2) + { + real[] s1 = *cast(real[]*)p1; + real[] s2 = *cast(real[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + int c = TypeInfo_e._compare(s1[u], s2[u]); + if (c) + return c; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + size_t tsize() + { + return (real[]).sizeof; + } + + uint flags() + { + return 1; + } + + TypeInfo next() + { + return typeid(real); + } +} + +// ireal[] + +class TypeInfo_Aj : TypeInfo_Ae +{ + char[] toString() { return "ireal[]"; } + + TypeInfo next() + { + return typeid(ireal); + } +} diff --git a/kernel/runtime/std/typeinfo/ti_Ashort.d b/kernel/runtime/std/typeinfo/ti_Ashort.d new file mode 100644 index 0000000..39a4672 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_Ashort.d @@ -0,0 +1,134 @@ + +module std.typeinfo.ti_Ashort; + +import kernel.runtime.util; + +// short[] + +class TypeInfo_As : TypeInfo +{ + char[] toString() { return "short[]"; } + + hash_t getHash(void *p) + { short[] s = *cast(short[]*)p; + size_t len = s.length; + short *str = s.ptr; + hash_t hash = 0; + + while (1) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash *= 9; + hash += *cast(ushort *)str; + return hash; + + default: + hash *= 9; + hash += *cast(uint *)str; + str += 2; + len -= 2; + break; + } + } + + return hash; + } + + int equals(void *p1, void *p2) + { + short[] s1 = *cast(short[]*)p1; + short[] s2 = *cast(short[]*)p2; + + return s1.length == s2.length && + memcmp(cast(ubyte*)s1.ptr, cast(ubyte*)s2.ptr, s1.length * short.sizeof) == 0; + } + + int compare(void *p1, void *p2) + { + short[] s1 = *cast(short[]*)p1; + short[] s2 = *cast(short[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + int result = s1[u] - s2[u]; + if (result) + return result; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + size_t tsize() + { + return (short[]).sizeof; + } + + uint flags() + { + return 1; + } + + TypeInfo next() + { + return typeid(short); + } +} + + +// ushort[] + +class TypeInfo_At : TypeInfo_As +{ + char[] toString() { return "ushort[]"; } + + int compare(void *p1, void *p2) + { + ushort[] s1 = *cast(ushort[]*)p1; + ushort[] s2 = *cast(ushort[]*)p2; + size_t len = s1.length; + + if (s2.length < len) + len = s2.length; + for (size_t u = 0; u < len; u++) + { + int result = s1[u] - s2[u]; + if (result) + return result; + } + if (s1.length < s2.length) + return -1; + else if (s1.length > s2.length) + return 1; + return 0; + } + + TypeInfo next() + { + return typeid(ushort); + } +} + +// wchar[] + +class TypeInfo_Au : TypeInfo_At +{ + char[] toString() { return "wchar[]"; } + + TypeInfo next() + { + return typeid(wchar); + } +} + + diff --git a/kernel/runtime/std/typeinfo/ti_C.d b/kernel/runtime/std/typeinfo/ti_C.d new file mode 100644 index 0000000..79f161c --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_C.d @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2004-2005 by Digital Mars, www.digitalmars.com + * Written by Walter Bright + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, in both source and binary form, subject to the following + * restrictions: + * + * o The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * o Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * o This notice may not be removed or altered from any source + * distribution. + */ + +module std.typeinfo.ti_C; + +// Object + +class TypeInfo_C : TypeInfo +{ + hash_t getHash(void *p) + { + Object o = *cast(Object*)p; + assert(o); + return o.toHash(); + } + + int equals(void *p1, void *p2) + { + Object o1 = *cast(Object*)p1; + Object o2 = *cast(Object*)p2; + + return o1 == o2; + } + + int compare(void *p1, void *p2) + { + Object o1 = *cast(Object*)p1; + Object o2 = *cast(Object*)p2; + int c = 0; + + // Regard null references as always being "less than" + if (!(o1 is o2)) + { + if (o1) + { if (!o2) + c = 1; + else + c = o1.opCmp(o2); + } + else + c = -1; + } + return c; + } + + size_t tsize() + { + return Object.sizeof; + } + + uint flags() + { + return 1; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_byte.d b/kernel/runtime/std/typeinfo/ti_byte.d new file mode 100644 index 0000000..e273a49 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_byte.d @@ -0,0 +1,39 @@ + +// byte + +module std.typeinfo.ti_byte; + +class TypeInfo_g : TypeInfo +{ + char[] toString() { return "byte"; } + + hash_t getHash(void *p) + { + return *cast(byte *)p; + } + + int equals(void *p1, void *p2) + { + return *cast(byte *)p1 == *cast(byte *)p2; + } + + int compare(void *p1, void *p2) + { + return *cast(byte *)p1 - *cast(byte *)p2; + } + + size_t tsize() + { + return byte.sizeof; + } + + void swap(void *p1, void *p2) + { + byte t; + + t = *cast(byte *)p1; + *cast(byte *)p1 = *cast(byte *)p2; + *cast(byte *)p2 = t; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_cdouble.d b/kernel/runtime/std/typeinfo/ti_cdouble.d new file mode 100644 index 0000000..e506d8c --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_cdouble.d @@ -0,0 +1,67 @@ + +// cdouble + +module std.typeinfo.ti_cdouble; + +class TypeInfo_r : TypeInfo +{ + char[] toString() { return "cdouble"; } + + hash_t getHash(void *p) + { + return (cast(uint *)p)[0] + (cast(uint *)p)[1] + + (cast(uint *)p)[2] + (cast(uint *)p)[3]; + } + + static int _equals(cdouble f1, cdouble f2) + { + return f1 == f2; + } + + static int _compare(cdouble f1, cdouble f2) + { int result; + + if (f1.re < f2.re) + result = -1; + else if (f1.re > f2.re) + result = 1; + else if (f1.im < f2.im) + result = -1; + else if (f1.im > f2.im) + result = 1; + else + result = 0; + return result; + } + + int equals(void *p1, void *p2) + { + return _equals(*cast(cdouble *)p1, *cast(cdouble *)p2); + } + + int compare(void *p1, void *p2) + { + return _compare(*cast(cdouble *)p1, *cast(cdouble *)p2); + } + + size_t tsize() + { + return cdouble.sizeof; + } + + void swap(void *p1, void *p2) + { + cdouble t; + + t = *cast(cdouble *)p1; + *cast(cdouble *)p1 = *cast(cdouble *)p2; + *cast(cdouble *)p2 = t; + } + + void[] init() + { static cdouble r; + + return (cast(cdouble *)&r)[0 .. 1]; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_cfloat.d b/kernel/runtime/std/typeinfo/ti_cfloat.d new file mode 100644 index 0000000..d4c6161 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_cfloat.d @@ -0,0 +1,66 @@ + +// cfloat + +module std.typeinfo.ti_cfloat; + +class TypeInfo_q : TypeInfo +{ + char[] toString() { return "cfloat"; } + + hash_t getHash(void *p) + { + return (cast(uint *)p)[0] + (cast(uint *)p)[1]; + } + + static int _equals(cfloat f1, cfloat f2) + { + return f1 == f2; + } + + static int _compare(cfloat f1, cfloat f2) + { int result; + + if (f1.re < f2.re) + result = -1; + else if (f1.re > f2.re) + result = 1; + else if (f1.im < f2.im) + result = -1; + else if (f1.im > f2.im) + result = 1; + else + result = 0; + return result; + } + + int equals(void *p1, void *p2) + { + return _equals(*cast(cfloat *)p1, *cast(cfloat *)p2); + } + + int compare(void *p1, void *p2) + { + return _compare(*cast(cfloat *)p1, *cast(cfloat *)p2); + } + + size_t tsize() + { + return cfloat.sizeof; + } + + void swap(void *p1, void *p2) + { + cfloat t; + + t = *cast(cfloat *)p1; + *cast(cfloat *)p1 = *cast(cfloat *)p2; + *cast(cfloat *)p2 = t; + } + + void[] init() + { static cfloat r; + + return (cast(cfloat *)&r)[0 .. 1]; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_char.d b/kernel/runtime/std/typeinfo/ti_char.d new file mode 100644 index 0000000..389d579 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_char.d @@ -0,0 +1,43 @@ + +module std.typeinfo.ti_char; + +class TypeInfo_a : TypeInfo +{ + char[] toString() { return "char"; } + + hash_t getHash(void *p) + { + return *cast(char *)p; + } + + int equals(void *p1, void *p2) + { + return *cast(char *)p1 == *cast(char *)p2; + } + + int compare(void *p1, void *p2) + { + return *cast(char *)p1 - *cast(char *)p2; + } + + size_t tsize() + { + return char.sizeof; + } + + void swap(void *p1, void *p2) + { + char t; + + t = *cast(char *)p1; + *cast(char *)p1 = *cast(char *)p2; + *cast(char *)p2 = t; + } + + void[] init() + { static char c; + + return (cast(char *)&c)[0 .. 1]; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_creal.d b/kernel/runtime/std/typeinfo/ti_creal.d new file mode 100644 index 0000000..9bd00cd --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_creal.d @@ -0,0 +1,68 @@ + +// creal + +module std.typeinfo.ti_creal; + +class TypeInfo_c : TypeInfo +{ + char[] toString() { return "creal"; } + + hash_t getHash(void *p) + { + return (cast(uint *)p)[0] + (cast(uint *)p)[1] + + (cast(uint *)p)[2] + (cast(uint *)p)[3] + + (cast(uint *)p)[4]; + } + + static int _equals(creal f1, creal f2) + { + return f1 == f2; + } + + static int _compare(creal f1, creal f2) + { int result; + + if (f1.re < f2.re) + result = -1; + else if (f1.re > f2.re) + result = 1; + else if (f1.im < f2.im) + result = -1; + else if (f1.im > f2.im) + result = 1; + else + result = 0; + return result; + } + + int equals(void *p1, void *p2) + { + return _equals(*cast(creal *)p1, *cast(creal *)p2); + } + + int compare(void *p1, void *p2) + { + return _compare(*cast(creal *)p1, *cast(creal *)p2); + } + + size_t tsize() + { + return creal.sizeof; + } + + void swap(void *p1, void *p2) + { + creal t; + + t = *cast(creal *)p1; + *cast(creal *)p1 = *cast(creal *)p2; + *cast(creal *)p2 = t; + } + + void[] init() + { static creal r; + + return (cast(creal *)&r)[0 .. 1]; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_dchar.d b/kernel/runtime/std/typeinfo/ti_dchar.d new file mode 100644 index 0000000..ae2d94a --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_dchar.d @@ -0,0 +1,45 @@ + +// dchar + +module std.typeinfo.ti_dchar; + +class TypeInfo_w : TypeInfo +{ + char[] toString() { return "dchar"; } + + hash_t getHash(void *p) + { + return *cast(dchar *)p; + } + + int equals(void *p1, void *p2) + { + return *cast(dchar *)p1 == *cast(dchar *)p2; + } + + int compare(void *p1, void *p2) + { + return *cast(dchar *)p1 - *cast(dchar *)p2; + } + + size_t tsize() + { + return dchar.sizeof; + } + + void swap(void *p1, void *p2) + { + dchar t; + + t = *cast(dchar *)p1; + *cast(dchar *)p1 = *cast(dchar *)p2; + *cast(dchar *)p2 = t; + } + + void[] init() + { static dchar c; + + return (cast(dchar *)&c)[0 .. 1]; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_delegate.d b/kernel/runtime/std/typeinfo/ti_delegate.d new file mode 100644 index 0000000..7ff8fa2 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_delegate.d @@ -0,0 +1,40 @@ + +// delegate + +module std.typeinfo.ti_delegate; + +alias void delegate(int) dg; + +class TypeInfo_D : TypeInfo +{ + hash_t getHash(void *p) + { long l = *cast(long *)p; + + return cast(uint)(l + (l >> 32)); + } + + int equals(void *p1, void *p2) + { + return *cast(dg *)p1 == *cast(dg *)p2; + } + + size_t tsize() + { + return dg.sizeof; + } + + void swap(void *p1, void *p2) + { + dg t; + + t = *cast(dg *)p1; + *cast(dg *)p1 = *cast(dg *)p2; + *cast(dg *)p2 = t; + } + + uint flags() + { + return 1; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_double.d b/kernel/runtime/std/typeinfo/ti_double.d new file mode 100644 index 0000000..0c7e0ab --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_double.d @@ -0,0 +1,68 @@ + +// double + +module std.typeinfo.ti_double; + +import kernel.runtime.util; +//private import std.math; + +class TypeInfo_d : TypeInfo +{ + char[] toString() { return "double"; } + + hash_t getHash(void *p) + { + return (cast(uint *)p)[0] + (cast(uint *)p)[1]; + } + + static int _equals(double f1, double f2) + { + return f1 == f2 || + (isnan(f1) && isnan(f2)); + } + + static int _compare(double d1, double d2) + { + if (d1 !<>= d2) // if either are NaN + { + if (isnan(d1)) + { if (isnan(d2)) + return 0; + return -1; + } + return 1; + } + return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1); + } + + int equals(void *p1, void *p2) + { + return _equals(*cast(double *)p1, *cast(double *)p2); + } + + int compare(void *p1, void *p2) + { + return _compare(*cast(double *)p1, *cast(double *)p2); + } + + size_t tsize() + { + return double.sizeof; + } + + void swap(void *p1, void *p2) + { + double t; + + t = *cast(double *)p1; + *cast(double *)p1 = *cast(double *)p2; + *cast(double *)p2 = t; + } + + void[] init() + { static double r; + + return (cast(double *)&r)[0 .. 1]; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_float.d b/kernel/runtime/std/typeinfo/ti_float.d new file mode 100644 index 0000000..bad76a3 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_float.d @@ -0,0 +1,68 @@ + +// float + +module std.typeinfo.ti_float; + +import kernel.runtime.util; +//private import std.math; + +class TypeInfo_f : TypeInfo +{ + char[] toString() { return "float"; } + + hash_t getHash(void *p) + { + return *cast(uint *)p; + } + + static int _equals(float f1, float f2) + { + return f1 == f2 || + (isnan(f1) && isnan(f2)); + } + + static int _compare(float d1, float d2) + { + if (d1 !<>= d2) // if either are NaN + { + if (isnan(d1)) + { if (isnan(d2)) + return 0; + return -1; + } + return 1; + } + return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1); + } + + int equals(void *p1, void *p2) + { + return _equals(*cast(float *)p1, *cast(float *)p2); + } + + int compare(void *p1, void *p2) + { + return _compare(*cast(float *)p1, *cast(float *)p2); + } + + size_t tsize() + { + return float.sizeof; + } + + void swap(void *p1, void *p2) + { + float t; + + t = *cast(float *)p1; + *cast(float *)p1 = *cast(float *)p2; + *cast(float *)p2 = t; + } + + void[] init() + { static float r; + + return (cast(float *)&r)[0 .. 1]; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_idouble.d b/kernel/runtime/std/typeinfo/ti_idouble.d new file mode 100644 index 0000000..4db37fa --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_idouble.d @@ -0,0 +1,12 @@ + +// idouble + +module std.typeinfo.ti_idouble; + +private import std.typeinfo.ti_double; + +class TypeInfo_p : TypeInfo_d +{ + char[] toString() { return "idouble"; } +} + diff --git a/kernel/runtime/std/typeinfo/ti_ifloat.d b/kernel/runtime/std/typeinfo/ti_ifloat.d new file mode 100644 index 0000000..877ba3e --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_ifloat.d @@ -0,0 +1,12 @@ + +// ifloat + +module std.typeinfo.ti_ifloat; + +private import std.typeinfo.ti_float; + +class TypeInfo_o : TypeInfo_f +{ + char[] toString() { return "ifloat"; } +} + diff --git a/kernel/runtime/std/typeinfo/ti_int.d b/kernel/runtime/std/typeinfo/ti_int.d new file mode 100644 index 0000000..0a94be4 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_int.d @@ -0,0 +1,43 @@ + +// int + +module std.typeinfo.ti_int; + +class TypeInfo_i : TypeInfo +{ + char[] toString() { return "int"; } + + hash_t getHash(void *p) + { + return *cast(uint *)p; + } + + int equals(void *p1, void *p2) + { + return *cast(uint *)p1 == *cast(uint *)p2; + } + + int compare(void *p1, void *p2) + { + if (*cast(int*) p1 < *cast(int*) p2) + return -1; + else if (*cast(int*) p1 > *cast(int*) p2) + return 1; + return 0; + } + + size_t tsize() + { + return int.sizeof; + } + + void swap(void *p1, void *p2) + { + int t; + + t = *cast(int *)p1; + *cast(int *)p1 = *cast(int *)p2; + *cast(int *)p2 = t; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_ireal.d b/kernel/runtime/std/typeinfo/ti_ireal.d new file mode 100644 index 0000000..9dd139f --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_ireal.d @@ -0,0 +1,12 @@ + +// ireal + +module std.typeinfo.ti_ireal; + +private import std.typeinfo.ti_real; + +class TypeInfo_j : TypeInfo_e +{ + char[] toString() { return "ireal"; } +} + diff --git a/kernel/runtime/std/typeinfo/ti_long.d b/kernel/runtime/std/typeinfo/ti_long.d new file mode 100644 index 0000000..e7cfa11 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_long.d @@ -0,0 +1,43 @@ + +// long + +module std.typeinfo.ti_long; + +class TypeInfo_l : TypeInfo +{ + char[] toString() { return "long"; } + + hash_t getHash(void *p) + { + return *cast(uint *)p + (cast(uint *)p)[1]; + } + + int equals(void *p1, void *p2) + { + return *cast(long *)p1 == *cast(long *)p2; + } + + int compare(void *p1, void *p2) + { + if (*cast(long *)p1 < *cast(long *)p2) + return -1; + else if (*cast(long *)p1 > *cast(long *)p2) + return 1; + return 0; + } + + size_t tsize() + { + return long.sizeof; + } + + void swap(void *p1, void *p2) + { + long t; + + t = *cast(long *)p1; + *cast(long *)p1 = *cast(long *)p2; + *cast(long *)p2 = t; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_ptr.d b/kernel/runtime/std/typeinfo/ti_ptr.d new file mode 100644 index 0000000..7e8844a --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_ptr.d @@ -0,0 +1,47 @@ + +// pointer + +module std.typeinfo.ti_ptr; + +class TypeInfo_P : TypeInfo +{ + hash_t getHash(void *p) + { + return cast(uint)*cast(void* *)p; + } + + int equals(void *p1, void *p2) + { + return *cast(void* *)p1 == *cast(void* *)p2; + } + + int compare(void *p1, void *p2) + { + auto c = *cast(void* *)p1 - *cast(void* *)p2; + if (c < 0) + return -1; + else if (c > 0) + return 1; + return 0; + } + + size_t tsize() + { + return (void*).sizeof; + } + + void swap(void *p1, void *p2) + { + void* t; + + t = *cast(void* *)p1; + *cast(void* *)p1 = *cast(void* *)p2; + *cast(void* *)p2 = t; + } + + uint flags() + { + return 1; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_real.d b/kernel/runtime/std/typeinfo/ti_real.d new file mode 100644 index 0000000..19c0d48 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_real.d @@ -0,0 +1,68 @@ + +// real + +module std.typeinfo.ti_real; + +import kernel.runtime.util; +//private import std.math; + +class TypeInfo_e : TypeInfo +{ + char[] toString() { return "real"; } + + hash_t getHash(void *p) + { + return (cast(uint *)p)[0] + (cast(uint *)p)[1] + (cast(ushort *)p)[4]; + } + + static int _equals(real f1, real f2) + { + return f1 == f2 || + (isnan(f1) && isnan(f2)); + } + + static int _compare(real d1, real d2) + { + if (d1 !<>= d2) // if either are NaN + { + if (isnan(d1)) + { if (isnan(d2)) + return 0; + return -1; + } + return 1; + } + return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1); + } + + int equals(void *p1, void *p2) + { + return _equals(*cast(real *)p1, *cast(real *)p2); + } + + int compare(void *p1, void *p2) + { + return _compare(*cast(real *)p1, *cast(real *)p2); + } + + size_t tsize() + { + return real.sizeof; + } + + void swap(void *p1, void *p2) + { + real t; + + t = *cast(real *)p1; + *cast(real *)p1 = *cast(real *)p2; + *cast(real *)p2 = t; + } + + void[] init() + { static real r; + + return (cast(real *)&r)[0 .. 1]; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_short.d b/kernel/runtime/std/typeinfo/ti_short.d new file mode 100644 index 0000000..6edbbb0 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_short.d @@ -0,0 +1,39 @@ + +// short + +module std.typeinfo.ti_short; + +class TypeInfo_s : TypeInfo +{ + char[] toString() { return "short"; } + + hash_t getHash(void *p) + { + return *cast(short *)p; + } + + int equals(void *p1, void *p2) + { + return *cast(short *)p1 == *cast(short *)p2; + } + + int compare(void *p1, void *p2) + { + return *cast(short *)p1 - *cast(short *)p2; + } + + size_t tsize() + { + return short.sizeof; + } + + void swap(void *p1, void *p2) + { + short t; + + t = *cast(short *)p1; + *cast(short *)p1 = *cast(short *)p2; + *cast(short *)p2 = t; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_ubyte.d b/kernel/runtime/std/typeinfo/ti_ubyte.d new file mode 100644 index 0000000..e4df88f --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_ubyte.d @@ -0,0 +1,43 @@ + +// ubyte + +module std.typeinfo.ti_ubyte; + +class TypeInfo_h : TypeInfo +{ + char[] toString() { return "ubyte"; } + + hash_t getHash(void *p) + { + return *cast(ubyte *)p; + } + + int equals(void *p1, void *p2) + { + return *cast(ubyte *)p1 == *cast(ubyte *)p2; + } + + int compare(void *p1, void *p2) + { + return *cast(ubyte *)p1 - *cast(ubyte *)p2; + } + + size_t tsize() + { + return ubyte.sizeof; + } + + void swap(void *p1, void *p2) + { + ubyte t; + + t = *cast(ubyte *)p1; + *cast(ubyte *)p1 = *cast(ubyte *)p2; + *cast(ubyte *)p2 = t; + } +} + +class TypeInfo_b : TypeInfo_h +{ + char[] toString() { return "bool"; } +} diff --git a/kernel/runtime/std/typeinfo/ti_uint.d b/kernel/runtime/std/typeinfo/ti_uint.d new file mode 100644 index 0000000..d24850e --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_uint.d @@ -0,0 +1,43 @@ + +// uint + +module std.typeinfo.ti_uint; + +class TypeInfo_k : TypeInfo +{ + char[] toString() { return "uint"; } + + hash_t getHash(void *p) + { + return *cast(uint *)p; + } + + int equals(void *p1, void *p2) + { + return *cast(uint *)p1 == *cast(uint *)p2; + } + + int compare(void *p1, void *p2) + { + if (*cast(uint*) p1 < *cast(uint*) p2) + return -1; + else if (*cast(uint*) p1 > *cast(uint*) p2) + return 1; + return 0; + } + + size_t tsize() + { + return uint.sizeof; + } + + void swap(void *p1, void *p2) + { + int t; + + t = *cast(uint *)p1; + *cast(uint *)p1 = *cast(uint *)p2; + *cast(uint *)p2 = t; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_ulong.d b/kernel/runtime/std/typeinfo/ti_ulong.d new file mode 100644 index 0000000..d54c605 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_ulong.d @@ -0,0 +1,43 @@ + +// ulong + +module std.typeinfo.ti_ulong; + +class TypeInfo_m : TypeInfo +{ + char[] toString() { return "ulong"; } + + hash_t getHash(void *p) + { + return *cast(uint *)p + (cast(uint *)p)[1]; + } + + int equals(void *p1, void *p2) + { + return *cast(ulong *)p1 == *cast(ulong *)p2; + } + + int compare(void *p1, void *p2) + { + if (*cast(ulong *)p1 < *cast(ulong *)p2) + return -1; + else if (*cast(ulong *)p1 > *cast(ulong *)p2) + return 1; + return 0; + } + + size_t tsize() + { + return ulong.sizeof; + } + + void swap(void *p1, void *p2) + { + ulong t; + + t = *cast(ulong *)p1; + *cast(ulong *)p1 = *cast(ulong *)p2; + *cast(ulong *)p2 = t; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_ushort.d b/kernel/runtime/std/typeinfo/ti_ushort.d new file mode 100644 index 0000000..6b7bd8a --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_ushort.d @@ -0,0 +1,39 @@ + +// ushort + +module std.typeinfo.ti_ushort; + +class TypeInfo_t : TypeInfo +{ + char[] toString() { return "ushort"; } + + hash_t getHash(void *p) + { + return *cast(ushort *)p; + } + + int equals(void *p1, void *p2) + { + return *cast(ushort *)p1 == *cast(ushort *)p2; + } + + int compare(void *p1, void *p2) + { + return *cast(ushort *)p1 - *cast(ushort *)p2; + } + + size_t tsize() + { + return ushort.sizeof; + } + + void swap(void *p1, void *p2) + { + ushort t; + + t = *cast(ushort *)p1; + *cast(ushort *)p1 = *cast(ushort *)p2; + *cast(ushort *)p2 = t; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_void.d b/kernel/runtime/std/typeinfo/ti_void.d new file mode 100644 index 0000000..dd4020d --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_void.d @@ -0,0 +1,44 @@ + +// void + +module std.typeinfo.ti_void; + +class TypeInfo_v : TypeInfo +{ + char[] toString() { return "void"; } + + hash_t getHash(void *p) + { + assert(0); + } + + int equals(void *p1, void *p2) + { + return *cast(byte *)p1 == *cast(byte *)p2; + } + + int compare(void *p1, void *p2) + { + return *cast(byte *)p1 - *cast(byte *)p2; + } + + size_t tsize() + { + return void.sizeof; + } + + void swap(void *p1, void *p2) + { + byte t; + + t = *cast(byte *)p1; + *cast(byte *)p1 = *cast(byte *)p2; + *cast(byte *)p2 = t; + } + + uint flags() + { + return 1; + } +} + diff --git a/kernel/runtime/std/typeinfo/ti_wchar.d b/kernel/runtime/std/typeinfo/ti_wchar.d new file mode 100644 index 0000000..95a71e2 --- /dev/null +++ b/kernel/runtime/std/typeinfo/ti_wchar.d @@ -0,0 +1,44 @@ + +module std.typeinfo.ti_wchar; + + +class TypeInfo_u : TypeInfo +{ + char[] toString() { return "wchar"; } + + hash_t getHash(void *p) + { + return *cast(wchar *)p; + } + + int equals(void *p1, void *p2) + { + return *cast(wchar *)p1 == *cast(wchar *)p2; + } + + int compare(void *p1, void *p2) + { + return *cast(wchar *)p1 - *cast(wchar *)p2; + } + + size_t tsize() + { + return wchar.sizeof; + } + + void swap(void *p1, void *p2) + { + wchar t; + + t = *cast(wchar *)p1; + *cast(wchar *)p1 = *cast(wchar *)p2; + *cast(wchar *)p2 = t; + } + + void[] init() + { static wchar c; + + return (cast(wchar *)&c)[0 .. 1]; + } +} + diff --git a/kernel/runtime/util.d b/kernel/runtime/util.d new file mode 100644 index 0000000..aa930d3 --- /dev/null +++ b/kernel/runtime/util.d @@ -0,0 +1,163 @@ +// This module contains helpful functions found necessary by the runtime and gcc. + +// contains: itoa, memcpy, memset, memmove, memcmp, strlen, isnan, toString + +module kernel.runtime.util; + +/** +This function converts an integer to a string, depending on the base passed in. + Params: + buf = The function will save the translated string into this character array. + base = The base of the integer value. If "d," it will be assumed to be decimal. If "x," the integer + will be hexadecimal. + d = The integer to translate. + Returns: The translated string in a character array. +*/ +char[] itoa(char[] buf, char base, long d) +{ + size_t p = buf.length - 1; + size_t startIdx = 0; + ulong ud = d; + bool negative = false; + + int divisor = 10; + + // If %d is specified and D is minus, put `-' in the head. + if(base == 'd' && d < 0) + { + negative = true; + ud = -d; + } + else if(base == 'x') + divisor = 16; + + // Divide UD by DIVISOR until UD == 0. + do + { + int remainder = ud % divisor; + buf[p--] = (remainder < 10) ? remainder + '0' : remainder + 'a' - 10; + } + while (ud /= divisor) + + if(negative) + buf[p--] = '-'; + + return buf[p + 1 .. $]; +} + +/** +This function copies data from a source piece of memory to a destination piece of memory. + Params: + dest = A pointer to the piece of memory serving as the copy destination. + src = A pointer to the piece of memory serving as the copy source. + count = The number of bytes to copy form src to dest. + Returns: A void pointer to the start of the destination data (dest). +*/ +extern(C) void* memcpy(void* dest, void* src, size_t count) +{ + ubyte* d = cast(ubyte*)dest; + ubyte* s = cast(ubyte*)src; + + for(size_t i = count; count; count--, d++, s++) + *d = *s; + + return dest; +} + +/** +Memcpy and memmove only really have differences at the user level, where they have slightly +different semantics. Here, they're pretty much the same. +*/ +extern(C) void* memmove(void* dest, void* src, size_t count) +{ + ubyte* d = cast(ubyte*)dest; + ubyte* s = cast(ubyte*)src; + + for(size_t i = count; count; count--, d++, s++) + *d = *s; + + return dest; +} + +/** +Compare two blocks of memory. + +Params: + a = Pointer to the first block. + b = Pointer to the second block. + n = The number of bytes to compare. + +Returns: + 0 if they are equal, < 0 if a is less than b, and > 0 if a is greater than b. +*/ +long memcmp(void* a, void* b, size_t n) +{ + ubyte* str_a = cast(ubyte*)a; + ubyte* str_b = cast(ubyte*)b; + + for(size_t i = 0; i < n; i++) + { + if(*str_a != *str_b) + return *str_a - *str_b; + + str_a++; + str_b++; + } + + return 0; +} + +/** +This function sets a particular piece of memory to a particular value. + Params: + addr = The address of the piece of memory you wish to write. + val = The value you wish to write to memory. + numBytes = The number of bytes you would like to write to memory. +*/ +void memset(void *addr, ubyte val, uint numBytes){ + ubyte *data = cast(ubyte*) addr; + + for(int i = 0; i < numBytes; i++){ + data[i] = val; + } +} + +/** +This function determines the size of a passed-in string. + Params: + s = A pointer to the beginning of a character array, declaring a string. + Returns: The size of the string in size_t format. +*/ +size_t strlen(char* s) +{ + size_t i = 0; + for( ; *s != 0; i++, s++){} + return i; +} + +/** +This function takes in a character pointer and returns a character array, or a string. + Params: + s = A pointer to the character(s) you wish to translate to a string. + Returns: A character array (string) containing the information. +*/ +char[] toString(char* s) +{ + return s[0 .. strlen(s)]; +} + +/** +This function checks to see if a floating point number is a NaN. + Params: + e = The value / piece of information you would like to check for number status. + Returns: + 0 if it isn't a NaN, non-zero if it is. +*/ +int isnan(real e) +{ + ushort* pe = cast(ushort *)&e; + ulong* ps = cast(ulong *)&e; + + return (pe[4] & 0x7FFF) == 0x7FFF && + *ps & 0x7FFFFFFFFFFFFFFF; +}