diff --git a/aaA.d b/aaA.d new file mode 100644 index 000000000..58ebc4e91 --- /dev/null +++ b/aaA.d @@ -0,0 +1,492 @@ +//_ aaAh4.d +// Copyright (c) 2000-2002 by Digital Mars +// Written by Walter Bright + +import c.stdio; +import c.stdlib; +import string; +import outofmemory; + +// Implementation of associative array + +struct Array +{ + int length; + void* ptr; +} + +struct aaA +{ + aaA *left; + aaA *right; + uint hash; + /* key */ + /* value */ +} + + +extern (C): + +/************************************************* + * Invariant for aa. + */ + +/+ +void _aaInvAh(aaA*[] aa) +{ + uint i; + + for (i = 0; i < aa.length; i++) + { + if (aa[i]) + _aaInvAh_x(aa[i]); + } +} + +private int _aaCmpAh_x(aaA *e1, aaA *e2) +{ int c; + + c = e1.hash - e2.hash; + if (c == 0) + { + c = e1.key.length - e2.key.length; + if (c == 0) + c = memcmp((char *)e1.key, (char *)e2.key, e1.key.length); + } + return c; +} + +private void _aaInvAh_x(aaA *e) +{ + uint key_hash; + aaA *e1; + aaA *e2; + + key_hash = getHash(e.key); + assert(key_hash == e.hash); + + while (1) + { int c; + + e1 = e.left; + if (e1) + { + _aaInvAh_x(e1); // ordinary recursion + do + { + c = _aaCmpAh_x(e1, e); + assert(c < 0); + e1 = e1.right; + } while (e1 != null); + } + + e2 = e.right; + if (e2) + { + do + { + c = _aaCmpAh_x(e, e2); + assert(c < 0); + e2 = e2.left; + } while (e2 != null); + e = e.right; // tail recursion + } + else + break; + } +} ++/ + +/**************************************************** + * Determine number of entries in associative array. + */ + +int _aaLen(aaA*[] aa) + in + { + //printf("_aaLen()+\n"); + //_aaInv(aa); + } + out (result) + { + assert(result >= 0); + //printf("_aaLen()-\n"); + } + body + { + int len = 0; + uint i; + + for (i = 0; i < aa.length; i++) + { + if (aa[i]) + len += _aaLen_x(aa[i]); + } + return len; + } + +private int _aaLen_x(aaA *e) +{ + int len = 1; + + while (1) + { + if (e.right) + len += _aaLen_x(e.right); + e = e.left; + if (!e) + break; + len++; + } + return len; +} + +/************************************************* + * Get pointer to value in associative array indexed by key. + * Add entry for key if it is not already there. + */ + +void *_aaGet(aaA*[] *aa, TypeInfo keyti, int valuesize, ...) + in + { + assert(aa); + } + out (result) + { + assert(result); + assert((*aa).length); + //assert(_aaInAh(*aa, key)); + assert(*aa); + } + body + { + void *pkey = (void *)(&valuesize + 1); + uint key_hash; + uint i; + aaA *e; + aaA **pe; + int keysize = keyti.tsize(); + + if (!(*aa).length) + { + alias aaA *pa; + + *aa = new pa[10]; + } + + key_hash = keyti.getHash(pkey); + //printf("hash = %d\n", key_hash); + i = key_hash % (*aa).length; + pe = &(*aa)[i]; + while ((e = *pe) != null) + { int c; + + c = key_hash - e.hash; + if (c == 0) + { + c = keyti.compare(pkey, e + 1); + if (c == 0) + goto Lret; + } + + if (c < 0) + pe = &e.left; + else + pe = &e.right; + } + + // Not found, create new elem + //printf("create new one\n"); + e = (aaA *) (void*) new byte[aaA.size + keysize + valuesize]; + memcpy(e + 1, pkey, keysize); + e.hash = key_hash; + *pe = e; + Lret: + return (void *)(e + 1) + keysize; + } + +/************************************************* + * Determine if key is in aa. + * Returns: + * 0 not in aa + * !=0 in aa + */ + +int _aaIn(aaA*[] aa, TypeInfo keyti, ...) + in + { + } + out (result) + { + assert(result == 0 || result == 1); + } + body + { + void *pkey = (void *)(&keyti + 1); + uint key_hash; + uint i; + aaA *e; + + if (aa.length) + { + key_hash = keyti.getHash(pkey); + //printf("hash = %d\n", key_hash); + i = key_hash % aa.length; + e = aa[i]; + while (e != null) + { int c; + + c = key_hash - e.hash; + if (c == 0) + { + c = keyti.compare(pkey, e + 1); + if (c == 0) + return 1; + } + + if (c < 0) + e = e.left; + else + e = e.right; + } + } + + // Not found + return 0; + } + +/************************************************* + * Delete key entry in aa[]. + * If key is not in aa[], do nothing. + */ + +void _aaDel(aaA*[] aa, TypeInfo keyti, ...) + { + void *pkey = (void *)(&keyti + 1); + uint key_hash; + uint i; + aaA *e; + aaA **pe; + + if (aa.length) + { + key_hash = keyti.getHash(pkey); + //printf("hash = %d\n", key_hash); + i = key_hash % aa.length; + pe = &aa[i]; + while ((e = *pe) != null) // null means not found + { int c; + + c = key_hash - e.hash; + if (c == 0) + { + c = keyti.compare(pkey, e + 1); + if (c == 0) + { + if (!e.left && !e.right) + { + *pe = null; + } + else if (e.left && !e.right) + { + *pe = e.left; + e.left = null; + } + else if (!e.left && e.right) + { + *pe = e.right; + e.right = null; + } + else + { + *pe = e.left; + e.left = null; + do + pe = &(*pe).right; + while (*pe); + *pe = e.right; + e.right = null; + } + + // Should notify GC that e can be free'd now + break; + } + } + + if (c < 0) + pe = &e.left; + else + pe = &e.right; + } + } + } + + +/******************************************** + * Produce array of v byte values from aa. + */ + +Array _aaValues(aaA*[] aa, uint k, uint v) + { + uint resi; + Array a; + + a.length = _aaLen(aa); + a.ptr = new byte[a.length * v]; + resi = 0; + for (uint i = 0; i < aa.length; i++) + { + if (aa[i]) + _aaValues_x(aa[i], a.ptr, resi, k, v); + } + assert(resi == a.length); + return a; + } + +void _aaValues_x(aaA *e, void *ptr, inout uint resi, uint k, uint v) + { + do + { + memcpy(ptr + resi * v, (byte*)e + aaA.size + k, v); + resi++; + if (e.left) + _aaValues_x(e.left, ptr, resi, k, v); + e = e.right; + } while (e != null); + } + +/******************************************** + * Rehash an array. + */ + +aaA*[] _aaRehash(aaA*[]* paa, TypeInfo keyti) + in + { + //_aaInvAh(paa); + } + out (result) + { + //_aaInvAh(result); + } + body + { + int len; + aaA*[] aa; + aaA*[] newaa; + int i; + + aa = *paa; + len = _aaLen(aa); + if (len < 1) + len = 1; + newaa = new aaA*[len]; + + for (i = 0; i < aa.length; i++) + { + if (aa[i]) + _aaRehash_x(newaa, aa[i], keyti); + } + + *paa = newaa; + return newaa; + } + +private void _aaRehash_x(aaA*[] newaa, aaA *olde, TypeInfo keyti) +{ + aaA *left; + aaA *right; + + while (1) + { + left = olde.left; + right = olde.right; + olde.left = null; + olde.right = null; + + uint key_hash; + uint i; + aaA *e; + aaA **pe; + + //printf("insert('%s')\n", (char *)olde.key); + key_hash = olde.hash; + i = key_hash % newaa.length; + pe = &newaa[i]; + while ((e = *pe) != null) + { int c; + + c = key_hash - e.hash; + if (c == 0) + c = keyti.compare(olde, e); + if (c < 0) + pe = &e.left; + else if (c > 0) + pe = &e.right; + else + assert(0); + } + *pe = olde; + + if (right) + { + _aaRehash_x(newaa, right, keyti); + } + if (!left) + break; + olde = left; + } +} + + +/******************************************** + * Produce array of N byte keys from aa. + */ + +Array _aaKeys(aaA*[] aa, uint n) + { + uint len; + byte[] res; + uint i; + uint resi; + + len = _aaLen(aa); + res = new byte[len * n]; + resi = 0; + for (i = 0; i < aa.length; i++) + { + if (aa[i]) + _aaKeys_x(aa[i], res, resi, n); + } + assert(resi == len); + + Array a; + a.length = len; + a.ptr = res; + return a; + } + +private +void _aaKeys_x(aaA *e, byte[] res, inout uint resi, uint n) + in + { + assert(e); + assert(resi < res.length); + } + out + { + assert(resi <= res.length); + } + body + { + do + { + memcpy(&res[resi * n], cast(byte*)(e + 1), n); + resi++; + if (e.left) + _aaKeys_x(e.left, res, resi, n); + e = e.right; + } while (e != null); + } + + + diff --git a/aaAh4.d b/aaAh4.d deleted file mode 100644 index d6a4184f5..000000000 --- a/aaAh4.d +++ /dev/null @@ -1,565 +0,0 @@ -//_ aaAh4.d -// Copyright (c) 2000-2001 by Digital Mars -// Written by Walter Bright - -import c.stdio; -import c.stdlib; -import string; -import outofmemory; - -// Implementation of associative array: -// int aa[char[] key]; - -struct aaAh4 -{ - aaAh4 *left; - aaAh4 *right; - uint hash; - char[] key; - int value; -} - -extern (C): - -/************************************************* - * Invariant for aa. - */ - -void _aaInvAh(aaAh4*[] aa) -{ - uint i; - - for (i = 0; i < aa.length; i++) - { - if (aa[i]) - _aaInvAh_x(aa[i]); - } -} - -private int _aaCmpAh_x(aaAh4 *e1, aaAh4 *e2) -{ int c; - - c = e1.hash - e2.hash; - if (c == 0) - { - c = e1.key.length - e2.key.length; - if (c == 0) - c = memcmp((char *)e1.key, (char *)e2.key, e1.key.length); - } - return c; -} - -private void _aaInvAh_x(aaAh4 *e) -{ - uint key_hash; - aaAh4 *e1; - aaAh4 *e2; - - key_hash = getHash(e.key); - assert(key_hash == e.hash); - - while (1) - { int c; - - e1 = e.left; - if (e1) - { - _aaInvAh_x(e1); // ordinary recursion - do - { - c = _aaCmpAh_x(e1, e); - assert(c < 0); - e1 = e1.right; - } while (e1 != null); - } - - e2 = e.right; - if (e2) - { - do - { - c = _aaCmpAh_x(e, e2); - assert(c < 0); - e2 = e2.left; - } while (e2 != null); - e = e.right; // tail recursion - } - else - break; - } -} - -/************************************************* - */ - -uint getHash(char[] s) -{ - uint hash; - uint len = s.length; - char *str = s; - - hash = 0; - while (1) - { - switch (len) - { - case 0: - return hash; - - case 1: - hash *= 9; - hash += *(ubyte *)str; - return hash; - - case 2: - hash *= 9; - hash += *(ushort *)str; - return hash; - - case 3: - hash *= 9; - hash += (*(ushort *)str << 8) + - ((ubyte *)str)[2]; - return hash; - - default: - hash *= 9; - hash += *(uint *)str; - str += 4; - len -= 4; - break; - } - } - return hash; -} - -/**************************************************** - * Determine number of entries in associative array. - */ - -int _aaLen(aaAh4*[] aa) - in - { - //printf("_aaLen()+\n"); - _aaInvAh(aa); - } - out (result) - { - assert(result >= 0); - //printf("_aaLen()-\n"); - } - body - { - int len = 0; - uint i; - - for (i = 0; i < aa.length; i++) - { - if (aa[i]) - len += _aaLen_x(aa[i]); - } - return len; - } - -private int _aaLen_x(aaAh4 *e) -{ - int len = 1; - - while (1) - { - if (e.right) - len += _aaLen_x(e.right); - e = e.left; - if (!e) - break; - len++; - } - return len; -} - -/************************************************* - * Get pointer to value in associative array indexed by key[]. - * Add entry for key if it is not already there. - */ - -int *_aaGetAh4(aaAh4*[] *aa, char[] key) - in - { - assert(aa); - } - out (result) - { - assert(result); - assert((*aa).length); - assert(_aaInAh(*aa, key)); - assert(*aa); - } - body - { - uint key_hash; - uint i; - aaAh4 *e; - aaAh4 **pe; - - if (!(*aa).length) - { - alias aaAh4 *pa; - - *aa = new pa[10]; - } - - key_hash = getHash(key); - //printf("hash = %d\n", key_hash); - i = key_hash % (*aa).length; - pe = &(*aa)[i]; - while ((e = *pe) != null) - { int c; - - c = key_hash - e->hash; - if (c == 0) - { - c = key.length - e->key.length; - if (c == 0) - { - c = memcmp((char *)key, (char *)e.key, key.length); - if (c == 0) - { - return &e->value; - } - } - } - - if (c < 0) - pe = &e->left; - else - pe = &e->right; - } - - // Not found, create new elem - //printf("create new one\n"); - e = (aaAh4 *) calloc(1, aaAh4.size); - if (!e) - _d_OutOfMemory(); - e->key = key; - e->hash = key_hash; - *pe = e; - return &e->value; - } - -/************************************************* - * Determine if key is in aa. - * Returns: - * 0 not in aa - * !=0 in aa - */ - -int _aaInAh(aaAh4*[] aa, char[] key) - in - { - } - out (result) - { - assert(result == 0 || result == 1); - } - body - { - uint key_hash; - uint i; - aaAh4 *e; - - if (aa.length) - { - key_hash = getHash(key); - //printf("hash = %d\n", key_hash); - i = key_hash % aa.length; - e = aa[i]; - while (e != null) - { int c; - - c = key_hash - e->hash; - if (c == 0) - { - c = key.length - e->key.length; - if (c == 0) - { - c = memcmp((char *)key, (char *)e.key, key.length); - if (c == 0) - return 1; - } - } - - if (c < 0) - e = e->left; - else - e = e->right; - } - } - - // Not found - return 0; - } - -/************************************************* - * Delete key entry in aa[]. - * If key is not in aa[], do nothing. - */ - -void _aaDelAh(aaAh4*[] aa, char[] key) - in - { - assert(aa); - } - out - { - assert(!_aaInAh(aa, key)); - assert(aa); - } - body - { - uint key_hash; - uint i; - aaAh4 *e; - aaAh4 **pe; - - if (aa.length) - { - key_hash = getHash(key); - //printf("hash = %d\n", key_hash); - i = key_hash % aa.length; - pe = &aa[i]; - while ((e = *pe) != null) // null means not found - { int c; - - c = key_hash - e->hash; - if (c == 0) - { - c = key.length - e->key.length; - if (c == 0) - { - c = memcmp((char *)key, (char *)e.key, key.length); - if (c == 0) - { - if (!e->left && !e->right) - { - *pe = null; - } - else if (e->left && !e->right) - { - *pe = e->left; - e->left = null; - } - else if (!e->left && e->right) - { - *pe = e->right; - e->right = null; - } - else - { - *pe = e->left; - e->left = null; - do - pe = &(*pe)->right; - while (*pe); - *pe = e->right; - e->right = null; - } - e.key = null; // enable GC to collect key[] - - // Should notify GC that e can be free'd now - break; - } - } - } - - if (c < 0) - pe = &e->left; - else - pe = &e->right; - } - } - } - -/******************************************** - * Produce array of 8 byte keys from aa. - */ - -char[][] _aaKeys8(aaAh4*[] aa) - in - { - } - out (result) - { - } - body - { - int len; - char[][] res; - uint i; - uint resi; - - len = _aaLen(aa); - res = new char[len][]; - resi = 0; - for (i = 0; i < aa.length; i++) - { - if (aa[i]) - _aaKeys8_x(aa[i], res, resi); - } - assert(resi == res.length); - return res; - } - -void _aaKeys8_x(aaAh4 *e, char[][] res, inout uint resi) - in - { - assert(e); - assert(resi < res.length); - } - out - { - assert(resi <= res.length); - } - body - { - do - { - res[resi++] = e.key; - if (e.left) - _aaKeys8_x(e.left, res, resi); - e = e.right; - } while (e != null); - } - -/******************************************** - * Produce array of 4 byte values from aa. - */ - -int[] _aaValues8_4(aaAh4*[] aa) - in - { - } - out (result) - { - } - body - { - int len; - int[] res; - uint i; - uint resi; - - len = _aaLen(aa); - res = new int[len]; - resi = 0; - for (i = 0; i < aa.length; i++) - { - if (aa[i]) - _aaValues8_4_x(aa[i], res, resi); - } - assert(resi == res.length); - return res; - } - -void _aaValues8_4_x(aaAh4 *e, int[] res, inout uint resi) - in - { - assert(e); - assert(resi < res.length); - } - out - { - assert(resi <= res.length); - } - body - { - do - { - res[resi++] = e.value; - if (e.left) - _aaValues8_4_x(e.left, res, resi); - e = e.right; - } while (e != null); - } - -/******************************************** - * Rehash an array. - */ - -aaAh4*[] _aaRehashAh(aaAh4*[] paa) - in - { - _aaInvAh(paa); - } - out (result) - { - _aaInvAh(result); - } - body - { - int len; - aaAh4*[] aa; - aaAh4*[] newaa; - int i; - - aa = paa; - len = _aaLen(aa); - if (len < 1) - len = 1; - newaa = new aaAh4*[len]; - - for (i = 0; i < aa.length; i++) - { - if (aa[i]) - _aaRehashAh_x(newaa, aa[i]); - } - - return newaa; - } - -void _aaRehashAh_x(aaAh4*[] aa, aaAh4 *olde) -{ - aaAh4 *left; - aaAh4 *right; - - while (1) - { - left = olde.left; - right = olde.right; - olde.left = null; - olde.right = null; - _aaRehashAh_ins(aa, olde); - if (right) - { - _aaRehashAh_x(aa, right); - } - if (!left) - break; - olde = left; - } -} - -void _aaRehashAh_ins(aaAh4*[] aa, aaAh4 *olde) -{ - uint key_hash; - uint i; - aaAh4 *e; - aaAh4 **pe; - - //printf("insert('%s')\n", (char *)olde.key); - i = olde.hash % aa.length; - pe = &aa[i]; - while ((e = *pe) != null) - { int c; - - c = _aaCmpAh_x(olde, e); - if (c < 0) - pe = &e.left; - else if (c > 0) - pe = &e.right; - else - assert(0); - } - - *pe = olde; -} - diff --git a/adi.d b/adi.d index e5b11852a..c09dce75e 100644 --- a/adi.d +++ b/adi.d @@ -1,66 +1,533 @@ -//_ ad.d Wed Dec 20 2000 -// Copyright (c) 2000 by Digital Mars +//_ adi.d +// Copyright (c) 2000-2002 by Digital Mars +// All Rights Reserved +// www.digitalmars.com // Written by Walter Bright +// Dynamic array property support routines + +//debug=adi; // uncomment to turn on debugging printf's + import c.stdio; import c.stdlib; import string; import outofmemory; -extern (C): -int _comparei(void *e1, void *e2) +struct Array { - return *(int *)e1 - *(int *)e2; + int length; + void *ptr; } -int[] _adsorti(int[] *pa) - in - { - assert(pa); - } + +/********************************************** + * Support for array.reverse property. + */ + +extern (C) Array _adReverse(Array a, int szelem) out (result) { - assert(result == *pa); - if (result.length) - for (int i = 0; i < result.length - 1; i++) - { - assert(result[i] <= result[i + 1]); - } + assert(result === a); } body { - qsort((*pa), (*pa).length, int.size, &_comparei); - return *pa; - } - -int[] _adreversei(int[] *pa) - in - { - assert(pa); - } - out (result) - { - assert(result == *pa); - } - body - { - int[] a = *pa; - if (a.length >= 2) { - int *lo = &a[0]; - int *hi = &a[a.length - 1]; + byte *tmp; + byte[16] buffer; - for (; lo < hi; lo++, hi--) + void* lo = a.ptr; + void* hi = a.ptr + (a.length - 1) * szelem; + + tmp = buffer; + if (szelem > 16) + tmp = (byte*) alloca(szelem); + + for (; lo < hi; lo += szelem, hi -= szelem) { - int tmp; - - tmp = *lo; - *lo = *hi; - *hi = tmp; + memcpy(tmp, lo, szelem); + memcpy(lo, hi, szelem); + memcpy(hi, tmp, szelem); } } return a; } +unittest +{ + debug(adi) printf("array.reverse.unittest\n"); + + int[] a = new int[5]; + int[] b; + int i; + + for (i = 0; i < 5; i++) + a[i] = i; + b = a.reverse; + assert(b === a); + for (i = 0; i < 5; i++) + assert(a[i] == 4 - i); + + struct X20 + { // More than 16 bytes in size + int a; + int b, c, d, e; + } + + X20[] c = new X20[5]; + X20[] d; + + for (i = 0; i < 5; i++) + { c[i].a = i; + c[i].e = 10; + } + d = c.reverse; + assert(d === c); + for (i = 0; i < 5; i++) + { + assert(c[i].a == 4 - i); + assert(c[i].e == 10); + } +} + +/********************************************** + * Support for array.reverse property for bit[]. + */ + +extern (C) bit[] _adReverseBit(bit[] a) + out (result) + { + assert(result === a); + } + body + { + if (a.length >= 2) + { + bit t; + int lo, hi; + + lo = 0; + hi = a.length - 1; + for (; lo < hi; lo++, hi--) + { + t = a[lo]; + a[lo] = a[hi]; + a[hi] = t; + } + } + return a; + } + +unittest +{ + debug(adi) printf("array.reverse_Bit[].unittest\n"); + + bit[] b; + b = new bit[5]; + static bit[5] data = [1,0,1,1,0]; + int i; + + b[] = data[]; + b.reverse; + for (i = 0; i < 5; i++) + { + assert(b[i] == data[4 - i]); + } +} + + +/********************************** + * Support for array.dup property. + */ + +extern (C) Array _adDup(Array a, int szelem) + out (result) + { + assert(memcmp(result.ptr, a.ptr, a.length * szelem) == 0); + } + body + { + Array r; + int size; + + size = a.length * szelem; + r.ptr = (void *) new byte[size]; + r.length = a.length; + memcpy(r.ptr, a.ptr, size); + return r; + } + +unittest +{ + int[] a; + int[] b; + int i; + + debug(adi) printf("array.dup.unittest\n"); + + a = new int[3]; + a[0] = 1; a[1] = 2; a[2] = 3; + b = a.dup; + assert(b.length == 3); + for (i = 0; i < 3; i++) + assert(b[i] == i + 1); +} + +/********************************** + * Support for array.dup property for bit[]. + */ + +extern (C) Array _adDupBit(Array a) + out (result) + { + assert(memcmp(result.ptr, a.ptr, (a.length + 7) / 8) == 0); + } + body + { + Array r; + int size; + + size = (a.length + 31) / 32; + r.ptr = (void *) new uint[size]; + r.length = a.length; + memcpy(r.ptr, a.ptr, size); + return r; + } + +unittest +{ + bit[] a; + bit[] b; + int i; + + debug(adi) printf("array.dupBit[].unittest\n"); + + a = new bit[3]; + a[0] = 1; a[1] = 0; a[2] = 1; + b = a.dup; + assert(b.length == 3); + for (i = 0; i < 3; i++) + { debug(adi) printf("b[%d] = %d\n", i, b[i]); + assert(b[i] == (((i ^ 1) & 1) ? true : false)); + } +} + + +/*************************************** + * Support for array equality test. + */ + +extern (C) int _adEq(Array a1, Array a2, TypeInfo ti) +{ + if (a1.length != a2.length) + return 0; // not equal + int sz = ti.tsize(); + //printf("sz = %d\n", sz); + void *p1 = a1.ptr; + void *p2 = a2.ptr; + for (int i = 0; i < a1.length; i++) + { + if (!ti.equals(p1 + i * sz, p2 + i * sz)) + return 0; // not equal + } + return 1; // equal +} + +unittest +{ + debug(adi) printf("array.Eq unittest\n"); + + char[] a = "hello"; + + assert(a != "hel"); + assert(a != "helloo"); + assert(a != "betty"); + assert(a == "hello"); + assert(a != "hxxxx"); +} + +/*************************************** + * Support for array equality test for bit arrays. + */ + +extern (C) int _adEqBit(Array a1, Array a2) +{ int i; + + if (a1.length != a2.length) + return 0; // not equal + byte *p1 = cast(byte*)a1.ptr; + byte *p2 = cast(byte*)a2.ptr; + uint n = a1.length / 8; + for (i = 0; i < n; i++) + { + if (p1[i] != p2[i]) + return 0; // not equal + } + + ubyte mask; + + n = a1.length & 7; + mask = (1 << n) - 1; + //printf("i = %d, n = %d, mask = %x, %x, %x\n", i, n, mask, p1[i], p2[i]); + return (mask == 0) || (p1[i] & mask) == (p2[i] & mask); +} + +unittest +{ + debug(adi) printf("array.EqBit unittest\n"); + + static bit[] a = [1,0,1,0,1]; + static bit[] b = [1,0,1]; + static bit[] c = [1,0,1,0,1,0,1]; + static bit[] d = [1,0,1,1,1]; + static bit[] e = [1,0,1,0,1]; + + assert(a != b); + assert(a != c); + assert(a != d); + assert(a == e); +} + +/*************************************** + * Support for array compare test. + */ + +extern (C) int _adCmp(Array a1, Array a2, TypeInfo ti) +{ + int len; + + //printf("adCmp()\n"); + len = a1.length; + if (a2.length < len) + len = a2.length; + int sz = ti.tsize(); + void *p1 = a1.ptr; + void *p2 = a2.ptr; + for (int i = 0; i < len; i++) + { + int c; + + c = ti.compare(p1 + i * sz, p2 + i * sz); + if (c) + return c; + } + return cast(int)a1.length - cast(int)a2.length; +} + +unittest +{ + debug(adi) printf("array.Cmp unittest\n"); + + char[] a = "hello"; + + assert(a > "hel"); + assert(a >= "hel"); + assert(a < "helloo"); + assert(a <= "helloo"); + assert(a > "betty"); + assert(a >= "betty"); + assert(a == "hello"); + assert(a <= "hello"); + assert(a >= "hello"); +} + +/*************************************** + * Support for array compare test. + */ + +extern (C) int _adCmpChar(Array a1, Array a2) +{ +version (X86) +{ + 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; + + //printf("adCmpChar()\n"); + len = a1.length; + if (a2.length < len) + len = a2.length; + c = string.memcmp((char *)a1.ptr, (char *)a2.ptr, len); + if (!c) + c = cast(int)a1.length - cast(int)a2.length; + return c; +} +} + +unittest +{ + debug(adi) printf("array.CmpChar unittest\n"); + + char[] a = "hello"; + + assert(a > "hel"); + assert(a >= "hel"); + assert(a < "helloo"); + assert(a <= "helloo"); + assert(a > "betty"); + assert(a >= "betty"); + assert(a == "hello"); + assert(a <= "hello"); + assert(a >= "hello"); +} + +/*************************************** + * Support for array compare test. + */ + +extern (C) int _adCmpBit(Array a1, Array a2) +{ + int len; + uint i; + + len = a1.length; + if (a2.length < len) + len = a2.length; + ubyte *p1 = cast(ubyte*)a1.ptr; + ubyte *p2 = cast(ubyte*)a2.ptr; + uint n = len / 8; + for (i = 0; i < n; i++) + { + if (p1[i] != p2[i]) + break; // not equal + } + for (uint j = i * 8; j < len; j++) + { ubyte mask = 1 << j; + int c; + + c = (int)(p1[i] & mask) - (int)(p2[i] & mask); + if (c) + return c; + } + return cast(int)a1.length - cast(int)a2.length; +} + +unittest +{ + debug(adi) printf("array.CmpBit unittest\n"); + + static bit[] a = [1,0,1,0,1]; + static bit[] b = [1,0,1]; + static bit[] c = [1,0,1,0,1,0,1]; + static bit[] d = [1,0,1,1,1]; + static bit[] e = [1,0,1,0,1]; + + assert(a > b); + assert(a >= b); + assert(a < c); + assert(a <= c); + assert(a < d); + assert(a <= d); + assert(a == e); + assert(a <= e); + assert(a >= e); +} + + diff --git a/array.d b/array.d new file mode 100644 index 000000000..b5bc45f08 --- /dev/null +++ b/array.d @@ -0,0 +1,43 @@ + +import object; +import c.stdio; + +class ArrayBoundsError : Object +{ + private: + + uint linnum; + char[] filename; + + this(char[] filename, uint linnum) + { + this.linnum = linnum; + this.filename = filename; + } + + public: + + /*************************************** + * If nobody catches the ArrayBoundsError, this winds up + * getting called by the startup code. + */ + + void print() + { + printf("ArrayBoundsError %s(%u)\n", (char *)filename, linnum); + } +} + + +/******************************************** + * Called by the compiler generated module assert function. + * Builds an ArrayBoundsError exception and throws it. + */ + +extern (C) static void _d_array_bounds(char[] filename, uint line) +{ + //printf("_d_assert(%s, %d)\n", (char *)filename, line); + ArrayBoundsError a = new ArrayBoundsError(filename, line); + //printf("assertion %p created\n", a); + throw a; +} diff --git a/arraycast.d b/arraycast.d new file mode 100644 index 000000000..bdca09f8e --- /dev/null +++ b/arraycast.d @@ -0,0 +1,78 @@ + + +/****************************************** + * Runtime helper to convert dynamic array of one + * type to dynamic array of another. + * Adjusts the length of the array. + * Throws exception if new length is not aligned. + */ + +extern (C) + +void[] _d_arraycast(uint tsize, uint fsize, void[] a) +{ + uint length = a.length; + uint nbytes; + + nbytes = length * fsize; + if (nbytes % tsize != 0) + { + throw new Error("array cast misalignment"); + } + length = nbytes / tsize; + *(uint *)&a = length; // jam new length + return a; +} + +unittest +{ + byte[int.size * 3] b; + int[] i; + short[] s; + + i = cast(int[])b; + assert(i.length == 3); + + s = cast(short[])b; + assert(s.length == 6); + + s = cast(short[])i; + assert(s.length == 6); +} + +/****************************************** + * Runtime helper to convert dynamic array of bits + * dynamic array of another. + * Adjusts the length of the array. + * Throws exception if new length is not aligned. + */ + +extern (C) + +void[] _d_arraycast_frombit(uint tsize, void[] a) +{ + uint length = a.length; + + if (length & 7) + { + throw new Error("bit[] array cast misalignment"); + } + length /= 8 * tsize; + *(uint *)&a = length; // jam new length + return a; +} + +unittest +{ + bit[int.size * 3 * 8] b; + int[] i; + short[] s; + + i = cast(int[])b; + assert(i.length == 3); + + s = cast(short[])b; + assert(s.length == 6); +} + + diff --git a/arraycat.d b/arraycat.d index a7cb39db6..e29e33300 100644 --- a/arraycat.d +++ b/arraycat.d @@ -24,32 +24,6 @@ byte[] _d_arraycat(byte[] x, byte[] y, uint size) return a; } -byte[] _d_arrayappend(byte[] *px, byte[] y, uint size) -{ - *px = _d_arraycat(*px, y, size); - return *px; -} - -byte[] _d_arrayappendc(inout byte[] x, in uint size, ...) -{ - byte[] a; - uint length; - void *argp; - - //printf("size = %d\n", size); - length = x.length + 1; - a = new byte[length * size]; - memcpy(a, x, x.length * size); - argp = &size + 1; - //printf("*argp = %llx\n", *(long *)argp); - memcpy(&a[x.length * size], argp, size); - //printf("a[0] = %llx\n", *(long *)&a[0]); - *(int *)&a = length; // jam length - //printf("a[0] = %llx\n", *(long *)&a[0]); - x = a; - return a; -} - byte[] _d_arraycopy(uint size, byte[] from, byte[] to) { //printf("f = %p,%d, t = %p,%d\n", (void*)from, from.length, (void*)to, to.length); @@ -70,3 +44,43 @@ byte[] _d_arraycopy(uint size, byte[] from, byte[] to) return to; } +bit[] _d_arraycopybit(bit[] from, bit[] to) +{ + //printf("f = %p,%d, t = %p,%d\n", (void*)from, from.length, (void*)to, to.length); + uint nbytes; + + if (to.length != from.length) + { + throw new Error("lengths don't match for array copy"); + } + else + { + nbytes = (to.length + 7) / 8; + if ((void *)to + nbytes <= (void *)from || + (void *)from + nbytes <= (void *)to) + { + memcpy((void *)to, (void *)from, nbytes); + } + else + { + throw new Error("overlapping array copy"); + } + } + return to; +} + +bit[] _d_arraysetbit(bit[] ba, uint lwr, uint upr, bit value) +in +{ + //printf("_d_arraysetbit(ba.length = %d, lwr = %u, upr = %u, value = %d)\n", ba.length, lwr, upr, value); + assert(lwr <= upr); + assert(upr <= ba.length); +} +body +{ + // Inefficient; lots of room for improvement here + for (uint i = lwr; i < upr; i++) + ba[i] = value; + + return ba; +} diff --git a/arraysetlength.cpp b/arraysetlength.cpp deleted file mode 100644 index 1f4825594..000000000 --- a/arraysetlength.cpp +++ /dev/null @@ -1,108 +0,0 @@ - -// D Language Runtime Library -// Copyright (c) 2001 by Digital Mars -// All Rights Reserved -// www.digitalmars.com - -// Do array resizing. - -#include -#include -#include - -#define __STDC__ 1 -#include - -#include "mars.h" - -extern GC gc; - -typedef struct Array -{ - unsigned length; - void *data; -} Array; - -extern "C" -{ - -/****************************** - * Resize dynamic arrays other than bit[]. - */ - -Array __ddecl __d_arraysetlength(unsigned newlength, unsigned sizeelem, Array *p) -{ - void *newdata; - unsigned newsize; - - //printf("p = %p, sizeelem = %d, newlength = %d\n", p, sizeelem, newlength); - - assert(sizeelem); - assert((p->data && p->length) || (!p->data && !p->length)); - if (newlength) - { - newsize = sizeelem * newlength; - newdata = (void *)gc.malloc(newsize); - if (!newdata) - _d_OutOfMemory(); - if (p->data) - { unsigned size; - - size = p->length * sizeelem; - if (newsize < size) - size = newsize; - else if (newsize > size) - memset((char *)newdata + size, 0, newsize - size); - memcpy(newdata, p->data, size); - } - else - memset(newdata, 0, newsize); - } - else - { - newdata = NULL; - } - - p->data = newdata; - p->length = newlength; - return *p; -} - -/*************************** - * Resize bit[] arrays. - */ - -Array __ddecl __d_arraysetlengthb(unsigned newlength, Array *p) -{ - void *newdata; - unsigned newsize; - - //printf("p = %p, newlength = %d\n", p, newlength); - - assert((p->data && p->length) || (!p->data && !p->length)); - if (newlength) - { - newsize = (newlength + 31) >> 5; // # of unsigned - newdata = (void *)gc.malloc(newsize * 4); - if (!newdata) - _d_OutOfMemory(); - if (p->data) - { unsigned size; - - size = (p->length + 31) >> 5; // # of unsigned - if (newsize < size) - size = newsize; - memcpy(newdata, p->data, size * 4); - } - } - else - { - newdata = NULL; - } - - p->data = newdata; - p->length = newlength; - return *p; -} - -} diff --git a/cast.d b/cast.d index 1b8f0baae..f43003187 100644 --- a/cast.d +++ b/cast.d @@ -27,18 +27,18 @@ Object _d_dynamic_cast(Object o, ClassInfo c) int _d_isbaseof(ClassInfo oc, ClassInfo c) { int i; - if (oc == c) + if (oc === c) return 1; do { - if (oc.base == c) + if (oc.base === c) return 1; for (i = 0; i < oc.interfaces.length; i++) { ClassInfo ic; ic = oc.interfaces[i].classinfo; - if (ic == c || _d_isbaseof(ic, c)) + if (ic === c || _d_isbaseof(ic, c)) return 1; } oc = oc.base; @@ -64,7 +64,7 @@ void *_d_interface_vtbl(ClassInfo ic, Object o) ClassInfo oic; oic = oc.interfaces[i].classinfo; - if (oic == ic) + if (oic === ic) { return (void *)oc.interfaces[i].vtbl; } diff --git a/com.d b/com.d index add313add..612792084 100644 --- a/com.d +++ b/com.d @@ -17,16 +17,16 @@ enum : int S_OK = 0, S_FALSE = 0x00000001, NOERROR = 0, - E_NOTIMPL = 0x80004001, - E_NOINTERFACE = 0x80004002, - E_POINTER = 0x80004003, - E_ABORT = 0x80004004, - E_FAIL = 0x80004005, - E_HANDLE = 0x80070006, - CLASS_E_NOAGGREGATION = 0x80040110, - E_OUTOFMEMORY = 0x8007000E, - E_INVALIDARG = 0x80070057, - E_UNEXPECTED = 0x8000FFFF, + E_NOTIMPL = cast(int)0x80004001, + E_NOINTERFACE = cast(int)0x80004002, + E_POINTER = cast(int)0x80004003, + E_ABORT = cast(int)0x80004004, + E_FAIL = cast(int)0x80004005, + E_HANDLE = cast(int)0x80070006, + CLASS_E_NOAGGREGATION = cast(int)0x80040110, + E_OUTOFMEMORY = cast(int)0x8007000E, + E_INVALIDARG = cast(int)0x80070057, + E_UNEXPECTED = cast(int)0x8000FFFF, } struct GUID { // size is 16 diff --git a/conv.d b/conv.d new file mode 100644 index 000000000..2fce8acff --- /dev/null +++ b/conv.d @@ -0,0 +1,833 @@ + +// conv.d +// Written by Walter Bright +// Copyright (c) 2002 Digital Mars +// All Rights Reserved +// www.digitalmars.com + +// Conversion building blocks. These differ from the C equivalents by +// checking for overflow and not allowing whitespace. + +//debug=conv; // uncomment to turn on debugging printf's + + + +/************** Exceptions ****************/ + +class ConvError : Error +{ + this(char[] s) + { + super("Error: conversion " ~ s); + } +} + +private void conv_error(char[] s) +{ + throw new ConvError(s); +} + +class ConvOverflowError : Error +{ + this(char[] s) + { + super("Error: overflow " ~ s); + } +} + +private void conv_overflow(char[] s) +{ + throw new ConvOverflowError(s); +} + +/*************************************************************** + * Convert character string to int. + * Grammar: + * ['+'|'-'] digit {digit} + */ + +int toInt(char[] s) +{ + int length = s.length; + + if (!length) + goto Lerr; + + int sign = 0; + int v = 0; + + for (int i = 0; i < length; i++) + { + char c = s[i]; + if (c >= '0' && c <= '9') + { + uint v1 = v; + v = v * 10 + (c - '0'); + if (cast(uint)v < v1) + goto Loverflow; + } + else if (c == '-' && i == 0) + { + sign = -1; + if (length == 1) + goto Lerr; + } + else if (c == '+' && i == 0) + { + if (length == 1) + goto Lerr; + } + else + goto Lerr; + } + if (sign == -1) + { + if (cast(uint)v > 0x80000000) + goto Loverflow; + v = -v; + } + else + { + if (cast(uint)v > 0x7FFFFFFF) + goto Loverflow; + } + return v; + +Loverflow: + conv_overflow(s); + +Lerr: + conv_error(s); + return 0; +} + +unittest +{ + debug(conv) printf("conv.toInt.unittest\n"); + + int i; + + i = toInt("0"); + assert(i == 0); + + i = toInt("+0"); + assert(i == 0); + + i = toInt("-0"); + assert(i == 0); + + i = toInt("6"); + assert(i == 6); + + i = toInt("+23"); + assert(i == 23); + + i = toInt("-468"); + assert(i == -468); + + i = toInt("2147483647"); + assert(i == 0x7FFFFFFF); + + i = toInt("-2147483648"); + assert(i == 0x80000000); + + static char[][] errors = + [ + "", + "-", + "+", + "-+", + " ", + " 0", + "0 ", + "- 0", + "1-", + "xx", + "123h", + "2147483648", + "-2147483649", + ]; + + for (int j = 0; j < errors.length; j++) + { + i = 47; + try + { + i = toInt(errors[j]); + printf("i = %d\n", i); + } + catch (Error e) + { + debug(conv) e.print(); + i = 3; + } + assert(i == 3); + } +} + + +/******************************************************* + * Convert character string to uint. + * Grammar: + * digit {digit} + */ + +uint toUint(char[] s) +{ + int length = s.length; + + if (!length) + goto Lerr; + + uint v = 0; + + for (int i = 0; i < length; i++) + { + char c = s[i]; + if (c >= '0' && c <= '9') + { + uint v1 = v; + v = v * 10 + (c - '0'); + if (v < v1) + goto Loverflow; + } + else + goto Lerr; + } + return v; + +Loverflow: + conv_overflow(s); + +Lerr: + conv_error(s); + return 0; +} + +unittest +{ + debug(conv) printf("conv.toUint.unittest\n"); + + uint i; + + i = toUint("0"); + assert(i == 0); + + i = toUint("6"); + assert(i == 6); + + i = toUint("23"); + assert(i == 23); + + i = toUint("468"); + assert(i == 468); + + i = toUint("2147483647"); + assert(i == 0x7FFFFFFF); + + i = toUint("4294967295"); + assert(i == 0xFFFFFFFF); + + static char[][] errors = + [ + "", + "-", + "+", + "-+", + " ", + " 0", + "0 ", + "- 0", + "1-", + "+5", + "-78", + "xx", + "123h", + "4294967296", + ]; + + for (int j = 0; j < errors.length; j++) + { + i = 47; + try + { + i = toUint(errors[j]); + printf("i = %d\n", i); + } + catch (Error e) + { + debug(conv) e.print(); + i = 3; + } + assert(i == 3); + } +} + +/*************************************************************** + * Convert character string to long. + * Grammar: + * ['+'|'-'] digit {digit} + */ + +long toLong(char[] s) +{ + int length = s.length; + + if (!length) + goto Lerr; + + int sign = 0; + long v = 0; + + for (int i = 0; i < length; i++) + { + char c = s[i]; + if (c >= '0' && c <= '9') + { + ulong v1 = v; + v = v * 10 + (c - '0'); + if (cast(ulong)v < v1) + goto Loverflow; + } + else if (c == '-' && i == 0) + { + sign = -1; + if (length == 1) + goto Lerr; + } + else if (c == '+' && i == 0) + { + if (length == 1) + goto Lerr; + } + else + goto Lerr; + } + if (sign == -1) + { + if (cast(ulong)v > 0x8000000000000000) + goto Loverflow; + v = -v; + } + else + { + if (cast(ulong)v > 0x7FFFFFFFFFFFFFFF) + goto Loverflow; + } + return v; + +Loverflow: + conv_overflow(s); + +Lerr: + conv_error(s); + return 0; +} + +unittest +{ + debug(conv) printf("conv.toLong.unittest\n"); + + long i; + + i = toLong("0"); + assert(i == 0); + + i = toLong("+0"); + assert(i == 0); + + i = toLong("-0"); + assert(i == 0); + + i = toLong("6"); + assert(i == 6); + + i = toLong("+23"); + assert(i == 23); + + i = toLong("-468"); + assert(i == -468); + + i = toLong("2147483647"); + assert(i == 0x7FFFFFFF); + + i = toLong("-2147483648"); + assert(i == -0x80000000L); + + i = toLong("9223372036854775807"); + assert(i == 0x7FFFFFFFFFFFFFFF); + + i = toLong("-9223372036854775808"); + assert(i == 0x8000000000000000); + + static char[][] errors = + [ + "", + "-", + "+", + "-+", + " ", + " 0", + "0 ", + "- 0", + "1-", + "xx", + "123h", + "9223372036854775808", + "-9223372036854775809", + ]; + + for (int j = 0; j < errors.length; j++) + { + i = 47; + try + { + i = toLong(errors[j]); + printf("l = %d\n", i); + } + catch (Error e) + { + debug(conv) e.print(); + i = 3; + } + assert(i == 3); + } +} + + +/******************************************************* + * Convert character string to ulong. + * Grammar: + * digit {digit} + */ + +ulong toUlong(char[] s) +{ + int length = s.length; + + if (!length) + goto Lerr; + + ulong v = 0; + + for (int i = 0; i < length; i++) + { + char c = s[i]; + if (c >= '0' && c <= '9') + { + ulong v1 = v; + v = v * 10 + (c - '0'); + if (v < v1) + goto Loverflow; + } + else + goto Lerr; + } + return v; + +Loverflow: + conv_overflow(s); + +Lerr: + conv_error(s); + return 0; +} + +unittest +{ + debug(conv) printf("conv.toUlong.unittest\n"); + + ulong i; + + i = toUlong("0"); + assert(i == 0); + + i = toUlong("6"); + assert(i == 6); + + i = toUlong("23"); + assert(i == 23); + + i = toUlong("468"); + assert(i == 468); + + i = toUlong("2147483647"); + assert(i == 0x7FFFFFFF); + + i = toUlong("4294967295"); + assert(i == 0xFFFFFFFF); + + i = toUlong("9223372036854775807"); + assert(i == 0x7FFFFFFFFFFFFFFF); + + i = toUlong("18446744073709551615"); + assert(i == 0xFFFFFFFFFFFFFFFF); + + + static char[][] errors = + [ + "", + "-", + "+", + "-+", + " ", + " 0", + "0 ", + "- 0", + "1-", + "+5", + "-78", + "xx", + "123h", + "18446744073709551616", + ]; + + for (int j = 0; j < errors.length; j++) + { + i = 47; + try + { + i = toUlong(errors[j]); + printf("i = %d\n", i); + } + catch (Error e) + { + debug(conv) e.print(); + i = 3; + } + assert(i == 3); + } +} + + +/*************************************************************** + * Convert character string to short. + * Grammar: + * ['+'|'-'] digit {digit} + */ + +short toShort(char[] s) +{ + int v = toInt(s); + + if (v != cast(short)v) + goto Loverflow; + + return cast(short)v; + +Loverflow: + conv_overflow(s); + return 0; +} + +unittest +{ + debug(conv) printf("conv.toShort.unittest\n"); + + short i; + + i = toShort("0"); + assert(i == 0); + + i = toShort("+0"); + assert(i == 0); + + i = toShort("-0"); + assert(i == 0); + + i = toShort("6"); + assert(i == 6); + + i = toShort("+23"); + assert(i == 23); + + i = toShort("-468"); + assert(i == -468); + + i = toShort("32767"); + assert(i == 0x7FFF); + + i = toShort("-32768"); + assert(i == cast(short)0x8000); + + static char[][] errors = + [ + "", + "-", + "+", + "-+", + " ", + " 0", + "0 ", + "- 0", + "1-", + "xx", + "123h", + "32768", + "-32769", + ]; + + for (int j = 0; j < errors.length; j++) + { + i = 47; + try + { + i = toShort(errors[j]); + printf("i = %d\n", i); + } + catch (Error e) + { + debug(conv) e.print(); + i = 3; + } + assert(i == 3); + } +} + + +/******************************************************* + * Convert character string to ushort. + * Grammar: + * digit {digit} + */ + +ushort toUshort(char[] s) +{ + uint v = toUint(s); + + if (v != cast(ushort)v) + goto Loverflow; + + return cast(ushort)v; + +Loverflow: + conv_overflow(s); + return 0; +} + +unittest +{ + debug(conv) printf("conv.toUshort.unittest\n"); + + ushort i; + + i = toUshort("0"); + assert(i == 0); + + i = toUshort("6"); + assert(i == 6); + + i = toUshort("23"); + assert(i == 23); + + i = toUshort("468"); + assert(i == 468); + + i = toUshort("32767"); + assert(i == 0x7FFF); + + i = toUshort("65535"); + assert(i == 0xFFFF); + + static char[][] errors = + [ + "", + "-", + "+", + "-+", + " ", + " 0", + "0 ", + "- 0", + "1-", + "+5", + "-78", + "xx", + "123h", + "65536", + ]; + + for (int j = 0; j < errors.length; j++) + { + i = 47; + try + { + i = toUshort(errors[j]); + printf("i = %d\n", i); + } + catch (Error e) + { + debug(conv) e.print(); + i = 3; + } + assert(i == 3); + } +} + + +/*************************************************************** + * Convert character string to byte. + * Grammar: + * ['+'|'-'] digit {digit} + */ + +byte toByte(char[] s) +{ + int v = toInt(s); + + if (v != cast(byte)v) + goto Loverflow; + + return cast(byte)v; + +Loverflow: + conv_overflow(s); + return 0; +} + +unittest +{ + debug(conv) printf("conv.toByte.unittest\n"); + + byte i; + + i = toByte("0"); + assert(i == 0); + + i = toByte("+0"); + assert(i == 0); + + i = toByte("-0"); + assert(i == 0); + + i = toByte("6"); + assert(i == 6); + + i = toByte("+23"); + assert(i == 23); + + i = toByte("-68"); + assert(i == -68); + + i = toByte("127"); + assert(i == 0x7F); + + i = toByte("-128"); + assert(i == cast(byte)0x80); + + static char[][] errors = + [ + "", + "-", + "+", + "-+", + " ", + " 0", + "0 ", + "- 0", + "1-", + "xx", + "123h", + "128", + "-129", + ]; + + for (int j = 0; j < errors.length; j++) + { + i = 47; + try + { + i = toByte(errors[j]); + printf("i = %d\n", i); + } + catch (Error e) + { + debug(conv) e.print(); + i = 3; + } + assert(i == 3); + } +} + + +/******************************************************* + * Convert character string to ubyte. + * Grammar: + * digit {digit} + */ + +ubyte toUbyte(char[] s) +{ + uint v = toUint(s); + + if (v != cast(ubyte)v) + goto Loverflow; + + return cast(ubyte)v; + +Loverflow: + conv_overflow(s); + return 0; +} + +unittest +{ + debug(conv) printf("conv.toUbyte.unittest\n"); + + ubyte i; + + i = toUbyte("0"); + assert(i == 0); + + i = toUbyte("6"); + assert(i == 6); + + i = toUbyte("23"); + assert(i == 23); + + i = toUbyte("68"); + assert(i == 68); + + i = toUbyte("127"); + assert(i == 0x7F); + + i = toUbyte("255"); + assert(i == 0xFF); + + static char[][] errors = + [ + "", + "-", + "+", + "-+", + " ", + " 0", + "0 ", + "- 0", + "1-", + "+5", + "-78", + "xx", + "123h", + "256", + ]; + + for (int j = 0; j < errors.length; j++) + { + i = 47; + try + { + i = toUbyte(errors[j]); + printf("i = %d\n", i); + } + catch (Error e) + { + debug(conv) e.print(); + i = 3; + } + assert(i == 3); + } +} + + + diff --git a/crc32.d b/crc32.d new file mode 100644 index 000000000..792ab4422 --- /dev/null +++ b/crc32.d @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2001, 2002 + * Pavel "EvilOne" Minayev + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Author makes no representations about + * the suitability of this software for any purpose. It is provided + * "as is" without express or implied warranty. + */ + +// CRC-32 calculation +module crc; + +static uint[256] crc32_table = +[ +0x00000000,0x77073096,0xee0e612c,0x990951ba,0x076dc419,0x706af48f,0xe963a535, +0x9e6495a3,0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988,0x09b64c2b,0x7eb17cbd, +0xe7b82d07,0x90bf1d91,0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de,0x1adad47d, +0x6ddde4eb,0xf4d4b551,0x83d385c7,0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec, +0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5,0x3b6e20c8,0x4c69105e,0xd56041e4, +0xa2677172,0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b,0x35b5a8fa,0x42b2986c, +0xdbbbc9d6,0xacbcf940,0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59,0x26d930ac, +0x51de003a,0xc8d75180,0xbfd06116,0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f, +0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924,0x2f6f7c87,0x58684c11,0xc1611dab, +0xb6662d3d,0x76dc4190,0x01db7106,0x98d220bc,0xefd5102a,0x71b18589,0x06b6b51f, +0x9fbfe4a5,0xe8b8d433,0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818,0x7f6a0dbb, +0x086d3d2d,0x91646c97,0xe6635c01,0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e, +0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457,0x65b0d9c6,0x12b7e950,0x8bbeb8ea, +0xfcb9887c,0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65,0x4db26158,0x3ab551ce, +0xa3bc0074,0xd4bb30e2,0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb,0x4369e96a, +0x346ed9fc,0xad678846,0xda60b8d0,0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9, +0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086,0x5768b525,0x206f85b3,0xb966d409, +0xce61e49f,0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4,0x59b33d17,0x2eb40d81, +0xb7bd5c3b,0xc0ba6cad,0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a,0xead54739, +0x9dd277af,0x04db2615,0x73dc1683,0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8, +0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1,0xf00f9344,0x8708a3d2,0x1e01f268, +0x6906c2fe,0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7,0xfed41b76,0x89d32be0, +0x10da7a5a,0x67dd4acc,0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5,0xd6d6a3e8, +0xa1d1937e,0x38d8c2c4,0x4fdff252,0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b, +0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60,0xdf60efc3,0xa867df55,0x316e8eef, +0x4669be79,0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236,0xcc0c7795,0xbb0b4703, +0x220216b9,0x5505262f,0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04,0xc2d7ffa7, +0xb5d0cf31,0x2cd99e8b,0x5bdeae1d,0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a, +0x9c0906a9,0xeb0e363f,0x72076785,0x05005713,0x95bf4a82,0xe2b87a14,0x7bb12bae, +0x0cb61b38,0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21,0x86d3d2d4,0xf1d4e242, +0x68ddb3f8,0x1fda836e,0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777,0x88085ae6, +0xff0f6a70,0x66063bca,0x11010b5c,0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45, +0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2,0xa7672661,0xd06016f7,0x4969474d, +0x3e6e77db,0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0,0xa9bcae53,0xdebb9ec5, +0x47b2cf7f,0x30b5ffe9,0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6,0xbad03605, +0xcdd70693,0x54de5729,0x23d967bf,0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94, +0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d +]; + +uint init_crc32() +{ + return cast(uint)-1; +} + +uint update_crc32(ubyte val, uint crc) +{ + return crc32_table[cast(ubyte) crc ^ val] ^ (crc >> 8); +} + +uint update_crc32(char val, uint crc) +{ + return update_crc32(cast(ubyte) val, crc); +} + +uint strcrc32(char[] s) +{ + uint crc = init_crc32(); + for (int i = 0; i < s.length; i++) + crc = update_crc32(s[i], crc); + return crc; +} diff --git a/date.d b/date.d new file mode 100644 index 000000000..271bb7162 --- /dev/null +++ b/date.d @@ -0,0 +1,681 @@ + +// Copyright (c) 1999-2002 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com + +import c.stdio; +import dateparse; + +alias long d_time; +//typedef double d_time; + +d_time d_time_nan = long.min; //double.nan; + +struct Date +{ + int year = int.min; // our "nan" Date value + int month; // 1..12 + int day; // 1..31 + int hour; // 0..23 + int minute; // 0..59 + int second; // 0..59 + int ms; // 0..999 + int weekday; // 0: not specified + // 1..7: Sunday..Saturday + int tzcorrection = int.min; // -12..12 correction in hours + + void parse(char[] s) + { + DateParse dp; + + dp.parse(s, *this); + } +} + +enum +{ + HoursPerDay = 24, + MinutesPerHour = 60, + msPerMinute = 60 * 1000, + msPerHour = 60 * msPerMinute, + msPerDay = 86400000, + TicksPerMs = 1, + TicksPerSecond = 1000, + TicksPerMinute = TicksPerSecond * 60, + TicksPerHour = TicksPerMinute * 60, + TicksPerDay = TicksPerHour * 24, +} + +d_time LocalTZA = 0; + + +const char[] daystr = "SunMonTueWedThuFriSat"; +const char[] monstr = "JanFebMarAprMayJunJulAugSepOctNovDec"; + +d_time floor(d_time d) +{ + return d; +} + +d_time dmod(d_time n, d_time d) +{ d_time r; + + r = n % d; + if (r < 0) + r += d; + return r; +} + +d_time HourFromTime(d_time t) +{ + return dmod(floor(t / msPerHour), HoursPerDay); +} + +d_time MinFromTime(d_time t) +{ + return dmod(floor(t / msPerMinute), MinutesPerHour); +} + +d_time SecFromTime(d_time t) +{ + return dmod(floor(t / TicksPerSecond), 60); +} + +d_time msFromTime(d_time t) +{ + return dmod(t / (TicksPerSecond / 1000), 1000); +} + +d_time TimeWithinDay(d_time t) +{ + return dmod(t, msPerDay); +} + +d_time toInteger(d_time n) +{ + return (n >= 0) + ? floor(n) + : - floor(-n); +} + +int Day(d_time t) +{ + return (int)floor(t / msPerDay); +} + +int LeapYear(int y) +{ + return ((y & 3) == 0 && + (y % 100 || (y % 400) == 0)); +} + +int DaysInYear(int y) +{ + return 365 + LeapYear(y); +} + +int DayFromYear(int y) +{ + return (int) (365 * (y - 1970) + + floor((y - 1969.0) / 4) - + floor((y - 1901.0) / 100) + + floor((y - 1601.0) / 400)); +} + +d_time TimeFromYear(int y) +{ + return (d_time)msPerDay * DayFromYear(y); +} + +int YearFromTime(d_time t) +{ int y; + + // Hazard a guess + y = 1970 + (int) (t / (365.2425 * msPerDay)); + + if (TimeFromYear(y) <= t) + { + while (TimeFromYear(y + 1) <= t) + y++; + } + else + { + do + { + y--; + } + while (TimeFromYear(y) > t); + } + return y; +} + +int inLeapYear(d_time t) +{ + return LeapYear(YearFromTime(t)); +} + +int MonthFromTime(d_time t) +{ + int day; + int month; + int year; + + year = YearFromTime(t); + day = Day(t) - DayFromYear(year); + + if (day < 59) + { + if (day < 31) + { assert(day >= 0); + month = 0; + } + else + month = 1; + } + else + { + day -= LeapYear(year); + if (day < 212) + { + if (day < 59) + month = 1; + else if (day < 90) + month = 2; + else if (day < 120) + month = 3; + else if (day < 151) + month = 4; + else if (day < 181) + month = 5; + else + month = 6; + } + else + { + if (day < 243) + month = 7; + else if (day < 273) + month = 8; + else if (day < 304) + month = 9; + else if (day < 334) + month = 10; + else if (day < 365) + month = 11; + else + { assert(0); + month = -1; // keep /W4 happy + } + } + } + return month; +} + +int DateFromTime(d_time t) +{ + int day; + int leap; + int month; + int year; + int date; + + year = YearFromTime(t); + day = Day(t) - DayFromYear(year); + leap = LeapYear(year); + month = MonthFromTime(t); + switch (month) + { + case 0: date = day + 1; break; + case 1: date = day - 30; break; + case 2: date = day - 58 - leap; break; + case 3: date = day - 89 - leap; break; + case 4: date = day - 119 - leap; break; + case 5: date = day - 150 - leap; break; + case 6: date = day - 180 - leap; break; + case 7: date = day - 211 - leap; break; + case 8: date = day - 242 - leap; break; + case 9: date = day - 272 - leap; break; + case 10: date = day - 303 - leap; break; + case 11: date = day - 333 - leap; break; + default: + assert(0); + date = -1; // keep /W4 happy + } + return date; +} + +int WeekDay(d_time t) +{ int w; + + w = ((int)Day(t) + 4) % 7; + if (w < 0) + w += 7; + return w; +} + +// Convert from UTC to local time + +d_time UTCtoLocalTime(d_time t) +{ + return t + LocalTZA + DaylightSavingTA(t); +} + +// Convert from local time to UTC + +d_time LocalTimetoUTC(d_time t) +{ + return t - LocalTZA - DaylightSavingTA(t - LocalTZA); +} + +d_time MakeTime(d_time hour, d_time min, d_time sec, d_time ms) +{ + /+ + if (!Port::isfinite(hour) || + !Port::isfinite(min) || + !Port::isfinite(sec) || + !Port::isfinite(ms)) + return d_time_nan; + +/ + + hour = toInteger(hour); + min = toInteger(min); + sec = toInteger(sec); + ms = toInteger(ms); + + return hour * TicksPerHour + + min * TicksPerMinute + + sec * TicksPerSecond + + ms * TicksPerMs; +} + + +d_time MakeDay(d_time year, d_time month, d_time date) +{ d_time t; + int y; + int m; + int leap; + static int mdays[12] = + [ 0,31,59,90,120,151,181,212,243,273,304,334 ]; + +/+ + if (!Port::isfinite(year) || + !Port::isfinite(month) || + !Port::isfinite(date)) + return d_time.init; + +/ + + year = toInteger(year); + month = toInteger(month); + date = toInteger(date); + + y = (int)(year + floor(month / 12)); + m = (int)dmod(month, 12); + + leap = LeapYear(y); + t = TimeFromYear(y) + (d_time)mdays[m] * msPerDay; + if (leap && month >= 2) + t += msPerDay; + + if (YearFromTime(t) != y || + MonthFromTime(t) != m || + DateFromTime(t) != 1) + return d_time_nan; + + return Day(t) + date - 1; +} + +d_time MakeDate(d_time day, d_time time) +{ + /+ + if (!Port::isfinite(day) || + !Port::isfinite(time)) + return d_time.init; + +/ + + return day * TicksPerDay + time; +} + +d_time TimeClip(d_time time) +{ + //printf("TimeClip(%g) = %g\n", time, toInteger(time)); + /+ + if (!Port::isfinite(time) || + time > 8.64e15 || + time < -8.64e15) + return d_time_nan; + +/ + return toInteger(time); +} + +char[] toString(d_time time) +{ + d_time t; + char sign; + int hr; + int mn; + int len; + d_time offset; + d_time dst; + + // Years are supposed to be -285616 .. 285616, or 7 digits + // "Tue Apr 02 02:04:57 GMT-0800 1996" + char[] buffer = new char[29 + 7 + 1]; + + /+ + if (Port::isnan(time)) + return "Invalid Date"; + +/ + + dst = DaylightSavingTA(time); + offset = LocalTZA + dst; + t = time + offset; + sign = '+'; + if (offset < 0) + { sign = '-'; +// offset = -offset; + offset = -(LocalTZA + dst); + } + + mn = (int)(offset / msPerMinute); + hr = mn / 60; + mn %= 60; + + //printf("hr = %d, offset = %g, LocalTZA = %g, dst = %g, + = %g\n", hr, offset, LocalTZA, dst, LocalTZA + dst); + + len = sprintf(buffer, "%.3s %.3s %02d %02d:%02d:%02d GMT%c%02d%02d %d", + &daystr[WeekDay(t) * 3], + &monstr[MonthFromTime(t) * 3], + DateFromTime(t), + (int)HourFromTime(t), (int)MinFromTime(t), (int)SecFromTime(t), + sign, hr, mn, + (long)YearFromTime(t)); + + // Ensure no buggy buffer overflows + //printf("len = %d, buffer.length = %d\n", len, buffer.length); + assert(len < buffer.length); + + return buffer[0 .. len]; +} + +char[] toDateString(d_time time) +{ + d_time t; + d_time offset; + d_time dst; + int len; + + // Years are supposed to be -285616 .. 285616, or 7 digits + // "Tue Apr 02 1996" + char[] buffer = new char[29 + 7 + 1]; + + /+ + if (Port::isnan(time)) + return "Invalid Date"; + +/ + + dst = DaylightSavingTA(time); + offset = LocalTZA + dst; + t = time + offset; + + len = sprintf(buffer, "%.3s %.3s %02d %d", + &daystr[WeekDay(t) * 3], + &monstr[MonthFromTime(t) * 3], + DateFromTime(t), + (long)YearFromTime(t)); + + // Ensure no buggy buffer overflows + assert(len < buffer.length); + + return buffer[0 .. len]; +} + +char[] toTimeString(d_time time) +{ + d_time t; + char sign; + int hr; + int mn; + int len; + d_time offset; + d_time dst; + + // "02:04:57 GMT-0800" + char[] buffer = new char[17 + 1]; + + /+ + if (Port::isnan(time)) + return "Invalid Date"; + +/ + + dst = DaylightSavingTA(time); + offset = LocalTZA + dst; + t = time + offset; + sign = '+'; + if (offset < 0) + { sign = '-'; +// offset = -offset; + offset = -(LocalTZA + dst); + } + + mn = (int)(offset / msPerMinute); + hr = mn / 60; + mn %= 60; + + //printf("hr = %d, offset = %g, LocalTZA = %g, dst = %g, + = %g\n", hr, offset, LocalTZA, dst, LocalTZA + dst); + + len = sprintf(buffer, "%02d:%02d:%02d GMT%c%02d%02d", + (int)HourFromTime(t), (int)MinFromTime(t), (int)SecFromTime(t), + sign, hr, mn); + + // Ensure no buggy buffer overflows + assert(len < buffer.length); + + // Lop off terminating 0 + return buffer[0 .. len]; +} + +d_time parse(char[] s) +{ + Date dp; + d_time n; + d_time day; + d_time time; + + try + { + dp.parse(s); + + //printf("year = %d, month = %d, day = %d\n", dp.year, dp.month, dp.day); + //printf("%02d:%02d:%02d.%03d\n", dp.hour, dp.minute, dp.second, dp.ms); + //printf("weekday = %d, ampm = %d, tzcorrection = %d\n", dp.weekday, dp.ampm, dp.tzcorrection); + + time = MakeTime(dp.hour, dp.minute, dp.second, dp.ms); + if (dp.tzcorrection == Date.tzcorrection.init) + time -= LocalTZA; + else + time += (d_time)dp.tzcorrection * msPerHour; + day = MakeDay(dp.year, dp.month - 1, dp.day); + n = MakeDate(day,time); + n = TimeClip(n); + } + catch + { + n = d_time.init; // erroneous date string + } + return n; +} + +static this() +{ + LocalTZA = getLocalTZA(); + //printf("LocalTZA = %g, %g\n", LocalTZA, LocalTZA / msPerHour); +} + +version (Win32) +{ + + import windows; + //import c.time; + + d_time getUTCtime() + { + SYSTEMTIME st; + d_time n; + + GetSystemTime(&st); // get time in UTC + n = SYSTEMTIME2d_time(&st, 0); + return n; + //return c.time.time(null) * TicksPerSecond; + } + + static d_time SYSTEMTIME2d_time(SYSTEMTIME *st, d_time t) + { + d_time n; + d_time day; + d_time time; + + if (st.wYear) + { + time = MakeTime(st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); + day = MakeDay(st.wYear, st.wMonth - 1, st.wDay); + } + else + { // wDayOfWeek is weekday, wDay is which week in the month + int year; + int wd; + int mday; + int month; + d_time x; + + year = YearFromTime(t); + month = st.wMonth - 1; + x = MakeDay(year, month, 1); + wd = WeekDay(MakeDate(x, 0)); + + mday = (7 - wd + st.wDayOfWeek); + if (mday >= 7) + mday -= 7; + mday += (st.wDay - 1) * 7 + 1; + //printf("month = %d, wDayOfWeek = %d, wDay = %d, mday = %d\n", st.wMonth, st.wDayOfWeek, st.wDay, mday); + + day = MakeDay(year, month, mday); + time = 0; + } + n = MakeDate(day,time); + n = TimeClip(n); + return n; + } + + d_time getLocalTZA() + { + d_time t; + DWORD r; + TIME_ZONE_INFORMATION tzi; + + r = GetTimeZoneInformation(&tzi); + switch (r) + { + case TIME_ZONE_ID_STANDARD: + case TIME_ZONE_ID_DAYLIGHT: + //printf("bias = %d\n", tzi.Bias); + //printf("standardbias = %d\n", tzi.StandardBias); + //printf("daylightbias = %d\n", tzi.DaylightBias); + t = -(tzi.Bias + tzi.StandardBias) * (d_time)(60 * TicksPerSecond); + break; + + default: + t = 0; + break; + } + + return t; + } + + /* + * Get daylight savings time adjust for time dt. + */ + + int DaylightSavingTA(d_time dt) + { + int t; + DWORD r; + TIME_ZONE_INFORMATION tzi; + d_time ts; + d_time td; + + r = GetTimeZoneInformation(&tzi); + t = 0; + switch (r) + { + case TIME_ZONE_ID_STANDARD: + case TIME_ZONE_ID_DAYLIGHT: + if (tzi.StandardDate.wMonth == 0 || + tzi.DaylightDate.wMonth == 0) + break; + + ts = SYSTEMTIME2d_time(&tzi.StandardDate, dt); + td = SYSTEMTIME2d_time(&tzi.DaylightDate, dt); + + if (td <= dt && dt <= ts) + { + t = -tzi.DaylightBias * (60 * TicksPerSecond); + //printf("DST is in effect, %d\n", t); + } + else + { + //printf("no DST\n"); + } + break; + } + return t; + } +} + +version (linux) +{ + + import c.time; + import c.sys.time; + + d_time getUTCtime() + { timeval tv; + + if (gettimeofday(&tv, NULL)) + { // Some error happened - try time() instead + return c.time.time(null) * TicksPerSecond; + } + + return tv.tv_sec * TicksPerSecond + (tv.tv_usec / (1000000 / TicksPerSecond)); + } + + d_time getLocalTZA() + { + time_t t; + + c.time.time(&t); + localtime(&t); // this will set timezone + return -(timezone * TicksPerSecond); + } + + /* + * Get daylight savings time adjust for time dt. + */ + + int DaylightSavingTA(d_time dt) + { + struct tm *tmp; + time_t t; + + t = (time_t) (dt / TicksPerSecond); // BUG: need range check + tmp = localtime(&t); + if (tmp.tm_isdst > 0) + // BUG: Assume daylight savings time is plus one hour. + return 60 * 60 * TicksPerSecond; + + return 0; + } + +} + + diff --git a/dateparse.d b/dateparse.d index e7ec6ad63..5c6b4747c 100644 --- a/dateparse.d +++ b/dateparse.d @@ -1,618 +1,746 @@ +// Copyright (c) 1999-2002 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com + +import string; +import c.stdlib; +import date; + +//debug=log; + +class DateParseError : Error +{ + this(char[] s) + { + super("Invalid date string: " ~ s); + } +} + struct DateParse { - int year = ~0; - int month = 0; - int day = 0; - int hours = 0; - int minutes = 0; - int seconds = 0; - int ms = 0; - int weekday = 0; - int ampm = 0; - int tzcorrection = ~0; + void parse(char[] s, out Date date) + { + *this = DateParse.init; + + buffer = ((char *)alloca(s.length))[0 .. s.length]; + + debug(log) printf("DateParse.parse('%.*s')\n", s); + if (!parseString(s)) + { + goto Lerror; + } + + /+ + if (year == year.init) + year = 0; + else + +/ + debug(log) + printf("year = %d, month = %d, day = %d\n%02d:%02d:%02d.%03d\nweekday = %d, tzcorrection = %d\n", + year, month, day, + hours, minutes, seconds, ms, + weekday, tzcorrection); + if ( + year == year.init || + (month < 1 || month > 12) || + (day < 1 || day > 31) || + (hours < 0 || hours > 23) || + (minutes < 0 || minutes > 59) || + (seconds < 0 || seconds > 59) || + (tzcorrection != tzcorrection.init && + ((tzcorrection < -1200 || tzcorrection > 1200) || + (tzcorrection % 100))) + ) + { + Lerror: + throw new DateParseError(s); + } + + if (ampm) + { if (hours > 12) + goto Lerror; + if (hours < 12) + { + if (ampm == 2) // if P.M. + hours += 12; + } + else if (ampm == 1) // if 12am + { + hours = 0; // which is midnight + } + } + + if (tzcorrection != tzcorrection.init) + tzcorrection /= 100; + + if (year >= 0 && year <= 99) + year += 1900; + + date.year = year; + date.month = month; + date.day = day; + date.hour = hours; + date.minute = minutes; + date.second = seconds; + date.ms = ms; + date.weekday = weekday; + date.tzcorrection = tzcorrection; + } - int parse(tchar *s); private: - tchar[] s; - int p; - int number; - char[] buffer; // not tchar + int year = int.min; // our "nan" Date value + int month; // 1..12 + int day; // 1..31 + int hours; // 0..23 + int minutes; // 0..59 + int seconds; // 0..59 + int ms; // 0..999 + int weekday; // 1..7 + int ampm; // 0: not specified + // 1: AM + // 2: PM + int tzcorrection = int.min; // -12..12 correction in hours - enum DP + char[] s; + int si; + int number; + char[] buffer; + + enum DP : byte { - DPerr, - DPweekday, - DPmonth, - DPnumber, - DPend, - DPcolon, - DPminus, - DPslash, - DPampm, - DPplus, - DPtz, - DPdst, - DPdsttz, + err, + weekday, + month, + number, + end, + colon, + minus, + slash, + ampm, + plus, + tz, + dst, + dsttz, } -int nextToken() -{ int nest; - uint c; - int b; - int result = DPerr; + DP nextToken() + { int nest; + uint c; + int bi; + DP result = DP.err; - //printf("DateParse::nextToken()\n"); - for (;;) - { - if (p == s.length) + //message(DTEXT("DateParse::nextToken()\n")); + for (;;) { - result = DPend; - goto ret_inc; - } - - //printf("\t*p = '%c'\n", s[p]); - switch (s[p]) - { - case ':': result = DPcolon; goto ret_inc; - case '+': result = DPplus; goto ret_inc; - case '-': result = DPminus; goto ret_inc; - case '/': result = DPslash; goto ret_inc; -#if defined(DATE_DOT_DELIM) - case '.': result = DPslash; goto ret_inc; -#endif - ret_inc: - p++; - goto Lret; - - case ' ': - case \n: - case \r: - case \t: - case ',': -#if !defined(DATE_DOT_DELIM) - case '.': -#endif - p++; - break; - - case '(': // comment - nest = 1; - for (;;) - { - p++; - if (p == s.length) - goto Lret; // error - switch (*p) + //message(DTEXT("\t*p = '%c'\n"), *p); + assert(si <= s.length); + if (si == s.length) + return DP.end; + switch (s[si]) + { + case ':': result = DP.colon; goto ret_inc; + case '+': result = DP.plus; goto ret_inc; + case '-': result = DP.minus; goto ret_inc; + case '/': result = DP.slash; goto ret_inc; + case '.': + version(DATE_DOT_DELIM) { - case '(': - nest++; - break; - - case ')': - if (--nest == 0) - goto Lendofcomment; - break; + result = DP.slash; + goto ret_inc; } - } - Lendofcomment: - p++; - break; - - default: - number = 0; - for (; p < s.length; p++) - { - c = s[p]; - if (!(c >= '0' && c <= '9')) + else + { + si++; break; - result = DPnumber; - number = number * 10 + (c - '0'); - } - if (result == DPnumber) + } + + ret_inc: + si++; goto Lret; - b = 0; - while (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') - { - if (c < 'a') // if upper case - c += 'a' - 'A'; // to lower case - buffer[b] = (char) c; - b++; - do + case ' ': + case \n: + case \r: + case \t: + case ',': + si++; + break; + + case '(': // comment + nest = 1; + for (;;) { - p++; - if (p == s.length) - goto Lclassify; - c = s[p]; - } while (c == '.'); // ignore embedded '.'s - } - Lclassify: - result = classify(buffer[0 .. b]); - goto Lret; - } - } -Lret: - return result; -} + si++; + if (si == s.length) + goto Lret; // error + switch (s[si]) + { + case '(': + nest++; + break; -int classify(char[] id) -{ - struct DateID - { - char[] name; - short tok; - short value; - }; + case ')': + if (--nest == 0) + goto Lendofcomment; + break; - static DateID dateidtab[] = - { - {"january", DPmonth, 1}, - {"february", DPmonth, 2}, - {"march", DPmonth, 3}, - {"april", DPmonth, 4}, - {"may", DPmonth, 5}, - {"june", DPmonth, 6}, - {"july", DPmonth, 7}, - {"august", DPmonth, 8}, - {"september", DPmonth, 9}, - {"october", DPmonth, 10}, - {"november", DPmonth, 11}, - {"december", DPmonth, 12}, - {"jan", DPmonth, 1}, - {"feb", DPmonth, 2}, - {"mar", DPmonth, 3}, - {"apr", DPmonth, 4}, - {"jun", DPmonth, 6}, - {"jul", DPmonth, 7}, - {"aug", DPmonth, 8}, - {"sep", DPmonth, 9}, - {"sept", DPmonth, 9}, - {"oct", DPmonth, 10}, - {"nov", DPmonth, 11}, - {"dec", DPmonth, 12}, - - {"sunday", DPweekday, 1}, - {"monday", DPweekday, 2}, - {"tuesday", DPweekday, 3}, - {"tues", DPweekday, 3}, - {"wednesday", DPweekday, 4}, - {"wednes", DPweekday, 4}, - {"thursday", DPweekday, 5}, - {"thur", DPweekday, 5}, - {"thurs", DPweekday, 5}, - {"friday", DPweekday, 6}, - {"saturday", DPweekday, 7}, - - {"sun", DPweekday, 1}, - {"mon", DPweekday, 2}, - {"tue", DPweekday, 3}, - {"wed", DPweekday, 4}, - {"thu", DPweekday, 5}, - {"fri", DPweekday, 6}, - {"sat", DPweekday, 7}, - - {"am", DPampm, 1}, - {"pm", DPampm, 2}, - - {"gmt", DPtz, +000}, - {"ut", DPtz, +000}, - {"utc", DPtz, +000}, - {"wet", DPtz, +000}, - {"z", DPtz, +000}, - {"wat", DPtz, +100}, - {"a", DPtz, +100}, - {"at", DPtz, +200}, - {"b", DPtz, +200}, - {"c", DPtz, +300}, - {"ast", DPtz, +400}, - {"d", DPtz, +400}, - {"est", DPtz, +500}, - {"e", DPtz, +500}, - {"cst", DPtz, +600}, - {"f", DPtz, +600}, - {"mst", DPtz, +700}, - {"g", DPtz, +700}, - {"pst", DPtz, +800}, - {"h", DPtz, +800}, - {"yst", DPtz, +900}, - {"i", DPtz, +900}, - {"ahst", DPtz, +1000}, - {"cat", DPtz, +1000}, - {"hst", DPtz, +1000}, - {"k", DPtz, +1000}, - {"nt", DPtz, +1100}, - {"l", DPtz, +1100}, - {"idlw", DPtz, +1200}, - {"m", DPtz, +1200}, - - {"cet", DPtz, -100}, - {"fwt", DPtz, -100}, - {"met", DPtz, -100}, - {"mewt", DPtz, -100}, - {"swt", DPtz, -100}, - {"n", DPtz, -100}, - {"eet", DPtz, -200}, - {"o", DPtz, -200}, - {"bt", DPtz, -300}, - {"p", DPtz, -300}, - {"zp4", DPtz, -400}, - {"q", DPtz, -400}, - {"zp5", DPtz, -500}, - {"r", DPtz, -500}, - {"zp6", DPtz, -600}, - {"s", DPtz, -600}, - {"wast", DPtz, -700}, - {"t", DPtz, -700}, - {"cct", DPtz, -800}, - {"u", DPtz, -800}, - {"jst", DPtz, -900}, - {"v", DPtz, -900}, - {"east", DPtz, -1000}, - {"gst", DPtz, -1000}, - {"w", DPtz, -1000}, - {"x", DPtz, -1100}, - {"idle", DPtz, -1200}, - {"nzst", DPtz, -1200}, - {"nzt", DPtz, -1200}, - {"y", DPtz, -1200}, - - {"bst", DPdsttz, 000}, - {"adt", DPdsttz, +400}, - {"edt", DPdsttz, +500}, - {"cdt", DPdsttz, +600}, - {"mdt", DPdsttz, +700}, - {"pdt", DPdsttz, +800}, - {"ydt", DPdsttz, +900}, - {"hdt", DPdsttz, +1000}, - {"mest", DPdsttz, -100}, - {"mesz", DPdsttz, -100}, - {"sst", DPdsttz, -100}, - {"fst", DPdsttz, -100}, - {"wadt", DPdsttz, -700}, - {"eadt", DPdsttz, -1000}, - {"nzdt", DPdsttz, -1200}, - - {"dst", DPdst, 0}, - }; - - //printf("DateParse::classify('%.*s')\n", buffer); - - // Do a linear search. Yes, it would be faster with a binary - // one. - for (uint i = 0; i < dateidtab.length; i++) - { - if (cmp(dateidtab[i].name, id) == 0) - { - number = dateidtab[i].value; - return dateidtab[i].tok; - } - } - return DPerr; -} - -int DateParse::parseString(tchar[] s) -{ - int n1; - int dp; - int psave; - int result; - - //printf("DateParse::parseString('%.*s')\n", s); - this->s = s; - p = s; - dp = nextToken(); - for (;;) - { - //printf("\tdp = %d\n", dp); - switch (dp) - { - case DPend: - result = 1; - Lret: - return result; - - case DPerr: - case_error: - //printf("\terror\n"); - default: - result = 0; - goto Lret; - - case DPminus: - break; // ignore spurious '-' - - case DPweekday: - weekday = number; - break; - - case DPmonth: // month day, [year] - month = number; - dp = nextToken(); - if (dp == DPnumber) - { - day = number; - psave = p; - dp = nextToken(); - if (dp == DPnumber) - { - n1 = number; - dp = nextToken(); - if (dp == DPcolon) - { // back up, not a year - p = psave; + default: + break; } - else - { year = n1; - continue; - } - break; } - } - continue; + Lendofcomment: + si++; + break; - case DPnumber: - n1 = number; - dp = nextToken(); - switch (dp) - { - case DPend: - year = n1; - break; + default: + number = 0; + for (;;) + { + if (si == s.length) + // c cannot be undefined here + break; + c = s[si]; + if (!(c >= '0' && c <= '9')) + break; + result = DP.number; + number = number * 10 + (c - '0'); + si++; + } + if (result == DP.number) + goto Lret; - case DPminus: - case DPslash: // n1/ ? ? ? - dp = parseCalendarDate(n1); - if (dp == DPerr) - goto case_error; - break; - - case DPcolon: // hh:mm [:ss] [am | pm] - dp = parseTimeOfDay(n1); - if (dp == DPerr) - goto case_error; - break; - - case DPampm: - hours = n1; - minutes = 0; - seconds = 0; - ampm = number; - break; - - case DPmonth: - day = n1; - month = number; - dp = nextToken(); - if (dp == DPnumber) - { // day month year - year = number; - dp = nextToken(); - } - break; - - default: - year = n1; - break; - } - continue; + bi = 0; + bufloop: + while (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') + { + if (c < 'a') // if upper case + c += (uint)'a' - (uint)'A'; // to lower case + buffer[bi] = c; + bi++; + do + { + si++; + if (si == s.length) + break bufloop; + c = s[si]; + } while (c == '.'); // ignore embedded '.'s + } + result = classify(buffer[0 .. bi]); + goto Lret; + } } - dp = nextToken(); + Lret: + return result; } -} -int DateParse::parseCalendarDate(int n1) -{ - int n2; - int n3; - int dp; - - //printf("DateParse::parseCalendarDate(%d)\n", n1); - dp = nextToken(); - if (dp == DPmonth) // day/month + DP classify(char[] buf) { - day = n1; - month = number; - dp = nextToken(); - if (dp == DPnumber) - { // day/month year - year = number; - dp = nextToken(); - } - else if (dp == DPminus || dp == DPslash) - { // day/month/year - dp = nextToken(); - if (dp != DPnumber) - goto case_error; - year = number; - dp = nextToken(); - } - return dp; - } - if (dp != DPnumber) - goto case_error; - n2 = number; - //printf("\tn2 = %d\n", n2); - dp = nextToken(); - if (dp == DPminus || dp == DPslash) - { - dp = nextToken(); - if (dp != DPnumber) - goto case_error; - n3 = number; - //printf("\tn3 = %d\n", n3); - dp = nextToken(); - - // case1: year/month/day - // case2: month/day/year - int case1, case2; - - case1 = (n1 > 12 || - (n2 >= 1 && n2 <= 12) && - (n3 >= 1 && n3 <= 31)); - case2 = ((n1 >= 1 && n1 <= 12) && - (n2 >= 1 && n2 <= 31) || - n3 > 31); - if (case1 == case2) - goto case_error; - if (case1) + struct DateID { - year = n1; - month = n2; - day = n3; + char[] name; + DP tok; + short value; + } + + static DateID dateidtab[] = + [ + { "january", DP.month, 1}, + { "february", DP.month, 2}, + { "march", DP.month, 3}, + { "april", DP.month, 4}, + { "may", DP.month, 5}, + { "june", DP.month, 6}, + { "july", DP.month, 7}, + { "august", DP.month, 8}, + { "september", DP.month, 9}, + { "october", DP.month, 10}, + { "november", DP.month, 11}, + { "december", DP.month, 12}, + { "jan", DP.month, 1}, + { "feb", DP.month, 2}, + { "mar", DP.month, 3}, + { "apr", DP.month, 4}, + { "jun", DP.month, 6}, + { "jul", DP.month, 7}, + { "aug", DP.month, 8}, + { "sep", DP.month, 9}, + { "sept", DP.month, 9}, + { "oct", DP.month, 10}, + { "nov", DP.month, 11}, + { "dec", DP.month, 12}, + + { "sunday", DP.weekday, 1}, + { "monday", DP.weekday, 2}, + { "tuesday", DP.weekday, 3}, + { "tues", DP.weekday, 3}, + { "wednesday", DP.weekday, 4}, + { "wednes", DP.weekday, 4}, + { "thursday", DP.weekday, 5}, + { "thur", DP.weekday, 5}, + { "thurs", DP.weekday, 5}, + { "friday", DP.weekday, 6}, + { "saturday", DP.weekday, 7}, + + { "sun", DP.weekday, 1}, + { "mon", DP.weekday, 2}, + { "tue", DP.weekday, 3}, + { "wed", DP.weekday, 4}, + { "thu", DP.weekday, 5}, + { "fri", DP.weekday, 6}, + { "sat", DP.weekday, 7}, + + { "am", DP.ampm, 1}, + { "pm", DP.ampm, 2}, + + { "gmt", DP.tz, +000}, + { "ut", DP.tz, +000}, + { "utc", DP.tz, +000}, + { "wet", DP.tz, +000}, + { "z", DP.tz, +000}, + { "wat", DP.tz, +100}, + { "a", DP.tz, +100}, + { "at", DP.tz, +200}, + { "b", DP.tz, +200}, + { "c", DP.tz, +300}, + { "ast", DP.tz, +400}, + { "d", DP.tz, +400}, + { "est", DP.tz, +500}, + { "e", DP.tz, +500}, + { "cst", DP.tz, +600}, + { "f", DP.tz, +600}, + { "mst", DP.tz, +700}, + { "g", DP.tz, +700}, + { "pst", DP.tz, +800}, + { "h", DP.tz, +800}, + { "yst", DP.tz, +900}, + { "i", DP.tz, +900}, + { "ahst", DP.tz, +1000}, + { "cat", DP.tz, +1000}, + { "hst", DP.tz, +1000}, + { "k", DP.tz, +1000}, + { "nt", DP.tz, +1100}, + { "l", DP.tz, +1100}, + { "idlw", DP.tz, +1200}, + { "m", DP.tz, +1200}, + + { "cet", DP.tz, -100}, + { "fwt", DP.tz, -100}, + { "met", DP.tz, -100}, + { "mewt", DP.tz, -100}, + { "swt", DP.tz, -100}, + { "n", DP.tz, -100}, + { "eet", DP.tz, -200}, + { "o", DP.tz, -200}, + { "bt", DP.tz, -300}, + { "p", DP.tz, -300}, + { "zp4", DP.tz, -400}, + { "q", DP.tz, -400}, + { "zp5", DP.tz, -500}, + { "r", DP.tz, -500}, + { "zp6", DP.tz, -600}, + { "s", DP.tz, -600}, + { "wast", DP.tz, -700}, + { "t", DP.tz, -700}, + { "cct", DP.tz, -800}, + { "u", DP.tz, -800}, + { "jst", DP.tz, -900}, + { "v", DP.tz, -900}, + { "east", DP.tz, -1000}, + { "gst", DP.tz, -1000}, + { "w", DP.tz, -1000}, + { "x", DP.tz, -1100}, + { "idle", DP.tz, -1200}, + { "nzst", DP.tz, -1200}, + { "nzt", DP.tz, -1200}, + { "y", DP.tz, -1200}, + + { "bst", DP.dsttz, 000}, + { "adt", DP.dsttz, +400}, + { "edt", DP.dsttz, +500}, + { "cdt", DP.dsttz, +600}, + { "mdt", DP.dsttz, +700}, + { "pdt", DP.dsttz, +800}, + { "ydt", DP.dsttz, +900}, + { "hdt", DP.dsttz, +1000}, + { "mest", DP.dsttz, -100}, + { "mesz", DP.dsttz, -100}, + { "sst", DP.dsttz, -100}, + { "fst", DP.dsttz, -100}, + { "wadt", DP.dsttz, -700}, + { "eadt", DP.dsttz, -1000}, + { "nzdt", DP.dsttz, -1200}, + + { "dst", DP.dst, 0}, + ]; + + //message(DTEXT("DateParse::classify('%s')\n"), buf); + + // Do a linear search. Yes, it would be faster with a binary + // one. + for (uint i = 0; i < dateidtab.length; i++) + { + if (string.cmp(dateidtab[i].name, buf) == 0) + { + number = dateidtab[i].value; + return dateidtab[i].tok; + } + } + return DP.err; + } + + int parseString(char[] s) + { + int n1; + int dp; + int sisave; + int result; + + //message(DTEXT("DateParse::parseString('%ls')\n"), s); + this.s = s; + si = 0; + dp = nextToken(); + for (;;) + { + //message(DTEXT("\tdp = %d\n"), dp); + switch (dp) + { + case DP.end: + result = 1; + Lret: + return result; + + case DP.err: + case_error: + //message(DTEXT("\terror\n")); + default: + result = 0; + goto Lret; + + case DP.minus: + break; // ignore spurious '-' + + case DP.weekday: + weekday = number; + break; + + case DP.month: // month day, [year] + month = number; + dp = nextToken(); + if (dp == DP.number) + { + day = number; + sisave = si; + dp = nextToken(); + if (dp == DP.number) + { + n1 = number; + dp = nextToken(); + if (dp == DP.colon) + { // back up, not a year + si = sisave; + } + else + { year = n1; + continue; + } + break; + } + } + continue; + + case DP.number: + n1 = number; + dp = nextToken(); + switch (dp) + { + case DP.end: + year = n1; + break; + + case DP.minus: + case DP.slash: // n1/ ? ? ? + dp = parseCalendarDate(n1); + if (dp == DP.err) + goto case_error; + break; + + case DP.colon: // hh:mm [:ss] [am | pm] + dp = parseTimeOfDay(n1); + if (dp == DP.err) + goto case_error; + break; + + case DP.ampm: + hours = n1; + minutes = 0; + seconds = 0; + ampm = number; + break; + + case DP.month: + day = n1; + month = number; + dp = nextToken(); + if (dp == DP.number) + { // day month year + year = number; + dp = nextToken(); + } + break; + + default: + year = n1; + break; + } + continue; + } + dp = nextToken(); + } + } + + int parseCalendarDate(int n1) + { + int n2; + int n3; + int dp; + + debug(log) printf("DateParse.parseCalendarDate(%d)\n", n1); + dp = nextToken(); + if (dp == DP.month) // day/month + { + day = n1; + month = number; + dp = nextToken(); + if (dp == DP.number) + { // day/month year + year = number; + dp = nextToken(); + } + else if (dp == DP.minus || dp == DP.slash) + { // day/month/year + dp = nextToken(); + if (dp != DP.number) + goto case_error; + year = number; + dp = nextToken(); + } + return dp; + } + if (dp != DP.number) + goto case_error; + n2 = number; + //message(DTEXT("\tn2 = %d\n"), n2); + dp = nextToken(); + if (dp == DP.minus || dp == DP.slash) + { + dp = nextToken(); + if (dp != DP.number) + goto case_error; + n3 = number; + //message(DTEXT("\tn3 = %d\n"), n3); + dp = nextToken(); + + // case1: year/month/day + // case2: month/day/year + int case1, case2; + + case1 = (n1 > 12 || + (n2 >= 1 && n2 <= 12) && + (n3 >= 1 && n3 <= 31)); + case2 = ((n1 >= 1 && n1 <= 12) && + (n2 >= 1 && n2 <= 31) || + n3 > 31); + if (case1 == case2) + goto case_error; + if (case1) + { + year = n1; + month = n2; + day = n3; + } + else + { + month = n1; + day = n2; + year = n3; + } } else - { + { // must be month/day month = n1; day = n2; - year = n3; } + return dp; + + case_error: + return DP.err; } - else - { // must be month/day - month = n1; - day = n2; - } - return dp; -case_error: - return DPerr; -} - -int DateParse::parseTimeOfDay(int n1) -{ - int dp; - int sign; - - // 12am is midnight - // 12pm is noon - - //printf("DateParse::parseTimeOfDay(%d)\n", n1); - hours = n1; - dp = nextToken(); - if (dp != DPnumber) - goto case_error; - minutes = number; - dp = nextToken(); - if (dp == DPcolon) + int parseTimeOfDay(int n1) { + int dp; + int sign; + + // 12am is midnight + // 12pm is noon + + //message(DTEXT("DateParse::parseTimeOfDay(%d)\n"), n1); + hours = n1; dp = nextToken(); - if (dp != DPnumber) + if (dp != DP.number) goto case_error; - seconds = number; + minutes = number; dp = nextToken(); - } - else - seconds = 0; - - if (dp == DPampm) - { - ampm = number; - dp = nextToken(); - } - else if (dp == DPplus || dp == DPminus) - { - Loffset: - sign = (dp == DPminus) ? -1 : 1; - dp = nextToken(); - if (dp != DPnumber) - goto case_error; - tzcorrection = -sign * number; - dp = nextToken(); - } - else if (dp == DPtz) - { - tzcorrection = number; - dp = nextToken(); - if (number == 0 && (dp == DPplus || dp == DPminus)) - goto Loffset; - if (dp == DPdst) - { tzcorrection += 100; + if (dp == DP.colon) + { + dp = nextToken(); + if (dp != DP.number) + goto case_error; + seconds = number; dp = nextToken(); } - } - else if (dp == DPdsttz) - { - tzcorrection = number; - dp = nextToken(); - } + else + seconds = 0; - return dp; - -case_error: - return DPerr; -} - -int DateParse::parse(tchar[] s) -{ - buffer = new char[s.length]; - - //printf("DateParse::parse('%.*s')\n", s); - if (!parseString(s)) - goto Lerror; - -#if defined(DATE_OR_TIME) - if (year == ~0) - year = 0; - else -#endif - if ( - year == ~0 || - (month < 1 || month > 12) || - (day < 1 || day > 31) || - (hours < 0 || hours > 23) || - (minutes < 0 || minutes > 59) || - (seconds < 0 || seconds > 59) || - (tzcorrection != ~0 && - ((tzcorrection < -1200 || tzcorrection > 1200) || - (tzcorrection % 100))) - ) - { - Lerror: - return 0; - } - - if (ampm) - { if (hours > 12) - goto Lerror; - if (hours < 12) + if (dp == DP.ampm) { - if (ampm == 2) // if P.M. - hours += 12; + ampm = number; + dp = nextToken(); } - else if (ampm == 1) // if 12am + else if (dp == DP.plus || dp == DP.minus) { - //hours = 24; // which is midnight - hours = 0; // which is midnight + Loffset: + sign = (dp == DP.minus) ? -1 : 1; + dp = nextToken(); + if (dp != DP.number) + goto case_error; + tzcorrection = -sign * number; + dp = nextToken(); } + else if (dp == DP.tz) + { + tzcorrection = number; + dp = nextToken(); + if (number == 0 && (dp == DP.plus || dp == DP.minus)) + goto Loffset; + if (dp == DP.dst) + { tzcorrection += 100; + dp = nextToken(); + } + } + else if (dp == DP.dsttz) + { + tzcorrection = number; + dp = nextToken(); + } + + return dp; + + case_error: + return DP.err; } - if (tzcorrection != ~0) - tzcorrection /= 100; - - if (year >= 0 && year <= 99) - year += 1900; - - return 1; -} - } unittest { - DateParse dp = new DateParse(); + DateParse dp; + Date d; - dp.parse("March 10, 1959 12:00 -800"); - dp.parse("Tue Apr 02 02:04:57 GMT-0800 1996"); - dp.parse("March 14, -1980 21:14:50"); - dp.parse("Tue Apr 02 02:04:57 1996"); - dp.parse("Tue, 02 Apr 1996 02:04:57 G.M.T."); - dp.parse("December 31, 3000"); - dp.parse("Wed, 31 Dec 1969 16:00:00 GMT"); - dp.parse("1/1/1999 12:30 AM"); + dp.parse("March 10, 1959 12:00 -800", d); + assert(d.year == 1959); + assert(d.month == 3); + assert(d.day == 10); + assert(d.hour == 12); + assert(d.minute == 0); + assert(d.second == 0); + assert(d.ms == 0); + assert(d.weekday == 0); + assert(d.tzcorrection == 8); - printf("year = %d, month = %d, day = %d\n", dp.year, dp.month, dp.day); - printf("%02d:%02d:%02d.%03d\n", dp.hours, dp.minutes, dp.seconds, dp.ms); - printf("weekday = %d, ampm = %d, tzcorrection = %d\n", dp.weekday, dp.ampm, dp.tzcorrection); + dp.parse("Tue Apr 02 02:04:57 GMT-0800 1996", d); + assert(d.year == 1996); + assert(d.month == 4); + assert(d.day == 2); + assert(d.hour == 2); + assert(d.minute == 4); + assert(d.second == 57); + assert(d.ms == 0); + assert(d.weekday == 3); + assert(d.tzcorrection == 8); + + dp.parse("March 14, -1980 21:14:50", d); + assert(d.year == 1980); + assert(d.month == 3); + assert(d.day == 14); + assert(d.hour == 21); + assert(d.minute == 14); + assert(d.second == 50); + assert(d.ms == 0); + assert(d.weekday == 0); + assert(d.tzcorrection == int.min); + + dp.parse("Tue Apr 02 02:04:57 1996", d); + assert(d.year == 1996); + assert(d.month == 4); + assert(d.day == 2); + assert(d.hour == 2); + assert(d.minute == 4); + assert(d.second == 57); + assert(d.ms == 0); + assert(d.weekday == 3); + assert(d.tzcorrection == int.min); + + dp.parse("Tue, 02 Apr 1996 02:04:57 G.M.T.", d); + assert(d.year == 1996); + assert(d.month == 4); + assert(d.day == 2); + assert(d.hour == 2); + assert(d.minute == 4); + assert(d.second == 57); + assert(d.ms == 0); + assert(d.weekday == 3); + assert(d.tzcorrection == 0); + + dp.parse("December 31, 3000", d); + assert(d.year == 3000); + assert(d.month == 12); + assert(d.day == 31); + assert(d.hour == 0); + assert(d.minute == 0); + assert(d.second == 0); + assert(d.ms == 0); + assert(d.weekday == 0); + assert(d.tzcorrection == int.min); + + dp.parse("Wed, 31 Dec 1969 16:00:00 GMT", d); + assert(d.year == 1969); + assert(d.month == 12); + assert(d.day == 31); + assert(d.hour == 16); + assert(d.minute == 0); + assert(d.second == 0); + assert(d.ms == 0); + assert(d.weekday == 4); + assert(d.tzcorrection == 0); + + dp.parse("1/1/1999 12:30 AM", d); + assert(d.year == 1999); + assert(d.month == 1); + assert(d.day == 1); + assert(d.hour == 0); + assert(d.minute == 30); + assert(d.second == 0); + assert(d.ms == 0); + assert(d.weekday == 0); + assert(d.tzcorrection == int.min); + + debug(log) printf("year = %d, month = %d, day = %d\n%02d:%02d:%02d.%03d\nweekday = %d, tzcorrection = %d\n", + d.year, d.month, d.day, + d.hour, d.minute, d.second, d.ms, + d.weekday, d.tzcorrection); } + diff --git a/deh.c b/deh.c index cc83c6bd9..857494472 100644 --- a/deh.c +++ b/deh.c @@ -219,7 +219,7 @@ Object *_d_create_exception_object(ClassInfo *ci, char *msg) { Exception *exc; - exc = (Exception *)__d_newclass(ci); + exc = (Exception *)_d_newclass(ci); // BUG: what if _d_newclass() throws an out of memory exception? if (msg) diff --git a/file.d b/file.d index 65910c5cb..7548082d7 100644 --- a/file.d +++ b/file.d @@ -22,7 +22,7 @@ class FileError : Error this(char[] name, char[] message) { - msg = name ~ ": " ~ message; + super(name ~ ": " ~ message); } this(char[] name, uint errno) diff --git a/gc.d b/gc.d index 2c0820cf3..2b33da90a 100644 --- a/gc.d +++ b/gc.d @@ -1,58 +1,40 @@ +// Copyright (c) 1999-2002 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com -class GC -{ - /*********************************** - * Run a full garbage collection cycle. - */ +import gcstats; - void fullCollect(); +void addRoot(void *p); // add p to list of roots +void removeRoot(void *p); // remove p from list of roots - /*********************************** - * Run a generational garbage collection cycle. - * Takes less time than a fullcollect(), but isn't - * as effective. - */ +void addRange(void *pbot, void *ptop); // add range to scan for roots +void removeRange(void *pbot); // remove range - void genCollect(); +/*********************************** + * Run a full garbage collection cycle. + */ - /*********************************** - * If a collection hasn't been run in a while, - * run a collection. This is useful to embed - * in an idle loop or place in a low priority thread. - */ +void fullCollect(); - void lazyCollect(); +/*********************************** + * Run a generational garbage collection cycle. + * Takes less time than a fullcollect(), but isn't + * as effective. + */ - /*************************************** - * Disable and enable collections. They must be - * a matched pair, and can nest. - * By default collections are enabled. - */ +void genCollect(); +void genCollectNoStack(); - void disable(); - void enable(); +void minimize(); // minimize physical memory usage - /**************************************** - * Run all pending destructors. - */ +/*************************************** + * Disable and enable collections. They must be + * a matched pair, and can nest. + * By default collections are enabled. + */ - void runDestructors(); +void disable(); +void enable(); - /***************************** - * The stomper is a memory debugging aid. - * It helps flush out initialization and dangling pointer - * bugs by stomping on allocated and free'd memory. - * With the stomper running, it's extremely unlikely that deleted - * and collected memory will inadvertantly - * contain valid data. - * Stomping, of course, slows down execution, so - * it can be adjusted dynamically. - * level 0 no stomping, run at max speed - * 1 stomp on new's, delete's, - * cause array resizes to always copy & stomp - * 2 add sentinels before and after objects to detect - * over and underruns - */ - - void setStomper(int level); -} +void getStats(out GCStats stats); diff --git a/gc/bits.c b/gc/bits.c deleted file mode 100644 index abde2c879..000000000 --- a/gc/bits.c +++ /dev/null @@ -1,39 +0,0 @@ - -// Copyright (C) 2001 by Digital Mars -// All Rights Reserved -// Written by Walter Bright - -#include -#include - -#include "bits.h" - -GCBits::GCBits() -{ - data = NULL; - nwords = 0; - nbits = 0; -} - -GCBits::~GCBits() -{ - if (data) - ::free(data); - data = NULL; -} - -void GCBits::invariant() -{ - if (data) - { - assert(nwords * sizeof(*data) * 8 >= nbits); - } -} - -void GCBits::alloc(unsigned nbits) -{ - this->nbits = nbits; - nwords = (nbits + (BITS_PER_WORD - 1)) >> BITS_SHIFT; - data = (unsigned *)::calloc(nwords + 2, sizeof(unsigned)); - assert(data); -} diff --git a/gc/bits.h b/gc/bits.h deleted file mode 100644 index b1b4a8f97..000000000 --- a/gc/bits.h +++ /dev/null @@ -1,60 +0,0 @@ - -// Copyright (C) 2001 by Digital Mars -// All Rights Reserved -// Written by Walter Bright - -#include - -#if __DMC__ -// Inline bit operations -#include -#endif - -#if _MSC_VER - // Disable useless warnings about unreferenced functions - #pragma warning (disable : 4514) -#endif // _MSC_VER - - -#define BITS_PER_WORD 32 -#define BITS_SHIFT 5 -#define BITS_MASK 31 - -struct Mem; - -struct GCBits -{ - unsigned *data; - unsigned nwords; // allocated words in data[] excluding sentinals - unsigned nbits; // number of bits in data[] excluding sentinals - - GCBits(); - ~GCBits(); - void invariant(); - - void alloc(unsigned nbits); - -#if __DMC__ >= 0x825 - unsigned test(unsigned i) { return _inline_bt(data + 1, i); } - void set(unsigned i) { _inline_bts(data + 1, i); } - void clear(unsigned i) { _inline_btr(data + 1, i); } - unsigned testClear(unsigned i) { return _inline_btr(data + 1, i); } -#else - unsigned test(unsigned i) { return data[1 + (i >> BITS_SHIFT)] & (1 << (i & BITS_MASK)); } - void set(unsigned i) { data[1 + (i >> BITS_SHIFT)] |= (1 << (i & BITS_MASK)); } - void clear(unsigned i) { data[1 + (i >> BITS_SHIFT)] &= ~(1 << (i & BITS_MASK)); } - unsigned testClear(unsigned i) - { - unsigned *p = &data[1 + (i >> BITS_SHIFT)]; - unsigned mask = (1 << (i & BITS_MASK)); - unsigned result = *p & mask; - *p &= ~mask; - return result; - } -#endif - - void zero() { memset(data + 1, 0, nwords * sizeof(unsigned)); } - void copy(GCBits *f) { memcpy(data + 1, f->data + 1, nwords * sizeof(unsigned)); } - - unsigned *base() { return data + 1; } -}; diff --git a/gc/gc.c b/gc/gc.c deleted file mode 100644 index 7ae47ce24..000000000 --- a/gc/gc.c +++ /dev/null @@ -1,1945 +0,0 @@ -// -// Copyright (C) 2001 by Digital Mars -// All Rights Reserved -// Written by Walter Bright -// licensed to Chromium Communications - -/************** Debugging ***************************/ - -#define THREADINVARIANT 0 // check thread integrity -#define INVARIANT 0 // check class invariants -#define LOGGING 0 // log allocations / frees -#define MEMSTOMP 0 // stomp on memory -#define SENTINEL 0 // add underrun/overrrun protection -#define PTRCHECK 0 // 0: fast pointer checking - // 1: more pointer checking - // 2: thorough but slow pointer checking -#define USEROOT 0 // use root for printf - -/*************** Configuration *********************/ - -#define STACKGROWSDOWN 1 // 1: growing the stack means subtracting from the stack pointer - // (use 1 for Intel X86 CPUs) - // 0: growing the stack means adding to the stack pointer - -/***************************************************/ - - -#include -#include -#include -#include - -#include "gc.h" -#include "os.h" -#include "bits.h" - -#if USEROOT -#if defined linux - -#include "../root/printf.h" - -#elif defined _WIN32 - -#include "..\root\printf.h" - -#endif -#else -#define WPRINTF wprintf -#endif - -#define PAGESIZE 4096 -#define COMMITSIZE (4096*16) -#define POOLSIZE (4096*256) - -//#define printf 1 || printf - -#undef assert -#define assert(e) if (!(e)) _gc_assert(__LINE__) -void _gc_assert(unsigned line); - -static int zero = 0; // used to avoid complaints about assert(0) by MSVC - -#if LOGGING - -struct Log; - -struct LogArray -{ - unsigned dim; - unsigned allocdim; - Log *data; - - LogArray(); - ~LogArray(); - - void reserve(unsigned nentries); - void push(Log foo); - void remove(unsigned i); - unsigned find(void *p); - void copy(LogArray *from); -}; - -#endif - -enum Bins -{ - B_16, - B_32, - B_64, - B_128, - B_256, - B_512, - B_1024, - B_2048, - B_PAGE, // start of large alloc - B_PAGEPLUS, // continuation of large alloc - B_FREE, // free page - B_UNCOMMITTED, // memory not committed for this page - B_MAX -}; - -struct List -{ - List *next; -}; - -struct Range -{ - void *pbot; - void *ptop; -}; - -struct Pool -{ - char *baseAddr; - char *topAddr; - GCBits mark; - GCBits scan; - GCBits finals; - GCBits freebits; - - unsigned npages; - unsigned ncommitted; // ncommitted <= npages - unsigned char *pagetable; - - Pool(unsigned npages); - ~Pool(); - void invariant(); - - unsigned allocPages(unsigned n); - void freePages(unsigned pagenum, unsigned npages); - int cmp(Pool *); -}; - -struct Gcx -{ -#if THREADINVARIANT - pthread_t self; -# define thread_invariant(gcx) assert(gcx->self == pthread_self()) -#else -# define thread_invariant(gcx) ((void)0) -#endif - - unsigned nroots; - unsigned rootdim; - void **roots; - - unsigned nranges; - unsigned rangedim; - Range *ranges; - - unsigned noStack; // !=0 means don't scan stack - unsigned log; - unsigned anychanges; - char *stackBottom; - - char *minAddr; // min(baseAddr) - char *maxAddr; // max(topAddr) - - unsigned npools; - Pool **pooltable; - - List *bucket[B_MAX]; // free list for each size - - GC_FINALIZER finalizer; // finalizer function (one per GC) - - Gcx(); - ~Gcx(); - void invariant(); - - void addRoot(void *p); - void removeRoot(void *p); - - void addRange(void *pbot, void *ptop); // add range to scan for roots - void removeRange(void *pbot); // remove range - - Pool *findPool(void *p); - unsigned findSize(void *p); - static Bins findBin(unsigned size); - void *bigAlloc(unsigned size); - Pool *newPool(unsigned npages); - int allocPage(Bins bin); - void mark(void *pbot, void *ptop); - unsigned fullcollectshell(); - unsigned fullcollect(void *stackTop); - void doFinalize(void *p); - - - /***** Leak Detector ******/ -#if LOGGING - LogArray current; - LogArray prev; - - void log_init(); - void log_malloc(void *p, unsigned size); - void log_free(void *p); - void log_collect(); - void log_parent(void *p, void *parent); -#else - void log_init() { } - void log_malloc(void *p, unsigned size) { (void)p; (void)size; } - void log_free(void *p) { (void)p; } - void log_collect() { } - void log_parent(void *p, void *parent) { (void)p; (void)parent; } -#endif -}; - -void _gc_assert(unsigned line) -{ - (void)line; -#if USEROOT - WPRINTF(L"GC assert fail: gc.c(%d)\n", line); - exit(0); -#else - printf("GC assert fail: gc.c(%d)\n", line); - exit(0); -#endif -} - -#if __DMC__ -void __cdecl _assert(void *e,void *file,unsigned line) -{ - printf("GC assert fail: %s(%d)\n", file, line); -} -#endif - -const unsigned binsize[B_MAX] = { 16,32,64,128,256,512,1024,2048,4096 }; -const unsigned notbinsize[B_MAX] = { ~(16u-1),~(32u-1),~(64u-1),~(128u-1),~(256u-1), - ~(512u-1),~(1024u-1),~(2048u-1),~(4096u-1) }; - -/* ============================ SENTINEL =============================== */ - - -#if SENTINEL -# define SENTINEL_PRE 0xF4F4F4F4 // 32 bits -# define SENTINEL_POST 0xF5 // 8 bits -# define SENTINEL_EXTRA (2 * sizeof(unsigned) + 1) -# define sentinel_size(p) (((unsigned *)(p))[-2]) -# define sentinel_pre(p) (((unsigned *)(p))[-1]) -# define sentinel_post(p) (((unsigned char *)(p))[sentinel_size(p)]) - -void sentinel_init(void *p, unsigned size) -{ - sentinel_size(p) = size; - sentinel_pre(p) = SENTINEL_PRE; - sentinel_post(p) = SENTINEL_POST; -} - -void sentinel_invariant(void *p) -{ - assert(sentinel_pre(p) == SENTINEL_PRE); - assert(sentinel_post(p) == SENTINEL_POST); -} - -inline void *sentinel_add(void *p) -{ - return (void *)((char *)p + 2 * sizeof(unsigned)); -} - -inline void *sentinel_sub(void *p) -{ - return (void *)((char *)p - 2 * sizeof(unsigned)); -} - -#else - -# define SENTINEL_EXTRA 0 -# define sentinel_init(p,size) (void)(p) -# define sentinel_invariant(p) (void)(p) -# define sentinel_add(p) (void *)(p) -# define sentinel_sub(p) (void *)(p) - -#endif - -/* ============================ GC =============================== */ - -unsigned GC::line = 0; -char *GC::file = NULL; - -GC::GC() -{ - //printf("GC::GC()\n"); - gcx = NULL; -} - -GC::~GC() -{ -#if defined linux - //WPRINTF(L"Thread %x ", pthread_self()); - //WPRINTF(L"GC::~GC()\n"); -#endif - if (gcx) - { - Gcx *g = (Gcx *)gcx; - delete g; - } -} - -void GC::init() -{ - gcx = new Gcx(); - setStackBottom(os_query_stackBottom()); -} - -void GC::setStackBottom(void *p) -{ - thread_invariant(gcx); -#if STACKGROWSDOWN - if (p > gcx->stackBottom) -#else - if (p < gcx->stackBottom) -#endif - { - //WPRINTF(L"setStackBottom(%x)\n", p); - gcx->stackBottom = (char *)p; - } -} - -void GC::scanStaticData() -{ - void *pbot; - void *ptop; - unsigned nbytes; - - os_query_staticdataseg(&pbot, &nbytes); - ptop = (void *)((char *)pbot + nbytes); - addRange(pbot, ptop); -} - -char *GC::strdup(const char *s) -{ - unsigned len; - char *p = NULL; - - thread_invariant(gcx); - if (s) - { - len = strlen(s) + 1; - p = (char *)malloc(len); - if (p) - memcpy(p, s, len); - } - return p; -} - -void *GC::calloc(size_t size, size_t n) -{ - unsigned len; - void *p; - - thread_invariant(gcx); - len = size * n; - p = malloc(len); - if (p) - { //printf("calloc: %x len %d\n", p, len); - memset(p, 0, len); - } - return p; -} - -void *GC::realloc(void *p, size_t size) -{ - thread_invariant(gcx); - if (!size) - { if (p) - { free(p); - p = NULL; - } - } - else if (!p) - { - p = malloc(size); - } - else - { void *p2; - unsigned psize; - -//WPRINTF(L"GC::realloc(p = %x, size = %u)\n", p, size); - sentinel_invariant(p); -#if SENTINEL - psize = sentinel_size(p); - if (psize != size) -#else - psize = gcx->findSize(p); // find allocated size - if (psize < size || // if new size is bigger - psize > size * 2) // or less than half -#endif - { - p2 = malloc(size); - if (psize < size) - size = psize; - //WPRINTF(L"\tcopying %d bytes\n",size); - memcpy(p2, p, size); - //free(p); // causes 507 crash - p = p2; - } - } - return p; -} - -void *GC::malloc(size_t size) -{ void *p; - Bins bin; - - //WPRINTF(L"GC::malloc(size = %d, gcx = %p)\n", size, gcx); - assert(gcx); - //printf("gcx->self = %x, pthread_self() = %x\n", gcx->self, pthread_self()); - thread_invariant(gcx); - if (size) - { - size += SENTINEL_EXTRA; - - // Compute size bin - bin = gcx->findBin(size); - - if (bin < B_PAGE) - { - p = gcx->bucket[bin]; - if (p == NULL) - { - if (!gcx->allocPage(bin)) // try to find a new page - { - if (!gcx->fullcollectshell()) // collect to find a new page - { - //gcx->newPool(1); - } - } - if (!gcx->bucket[bin] && !gcx->allocPage(bin)) - { int result; - - gcx->newPool(1); // allocate new pool to find a new page - result = gcx->allocPage(bin); - if (!result) - return NULL; - } - p = gcx->bucket[bin]; - } - - // Return next item from free list - gcx->bucket[bin] = ((List *)p)->next; - memset((char *)p + size, 0, binsize[bin] - size); - //printf("\tmalloc => %x\n", p); - #if MEMSTOMP - memset(p, 0xF0, size); - #endif - } - else - { - p = gcx->bigAlloc(size); - if (!p) - return NULL; - } - size -= SENTINEL_EXTRA; - p = sentinel_add(p); - sentinel_init(p, size); - gcx->log_malloc(p, size); - return p; - } - return NULL; -} - -void GC::free(void *p) -{ - Pool *pool; - unsigned pagenum; - Bins bin; - unsigned bit; - - thread_invariant(gcx); - if (!p) - return; - - // Find which page it is in - pool = gcx->findPool(p); - if (!pool) // if not one of ours - return; // ignore - sentinel_invariant(p); - p = sentinel_sub(p); - pagenum = ((char *)p - pool->baseAddr) / PAGESIZE; - - if (pool->finals.nbits && gcx->finalizer) - { - bit = (unsigned)((char *)p - pool->baseAddr) / 16; - if (pool->finals.testClear(bit)) - { - (*gcx->finalizer)(sentinel_add(p), NULL); - } - } - - bin = (Bins)pool->pagetable[pagenum]; - if (bin == B_PAGE) // if large alloc - { int npages; - unsigned n; - - // Free pages - npages = 1; - n = pagenum; - while (++n < pool->ncommitted && pool->pagetable[n] == B_PAGEPLUS) - npages++; - #if MEMSTOMP - memset(p, 0xF2, npages * PAGESIZE); - #endif - pool->freePages(pagenum, npages); - } - else - { // Add to free list - List *list = (List *)p; - - #if MEMSTOMP - memset(p, 0xF2, binsize[bin]); - #endif - - list->next = gcx->bucket[bin]; - gcx->bucket[bin] = list; - } - gcx->log_free(sentinel_add(p)); -} - -void *GC::mallocdup(void *o, size_t size) -{ - void *p; - - thread_invariant(gcx); - p = malloc(size); - if (p) - return memcpy(p, o, size); - else - return NULL; -} - -/**************************************** - * Verify that pointer p: - * 1) belongs to this memory pool - * 2) points to the start of an allocated piece of memory - * 3) is not on a free list - */ - -void GC::check(void *p) -{ - if (p) - { - sentinel_invariant(p); -#if PTRCHECK >= 1 - Pool *pool; - unsigned pagenum; - Bins bin; - unsigned size; - - p = sentinel_sub(p); - pool = gcx->findPool(p); - assert(pool); - pagenum = ((char *)p - pool->baseAddr) / PAGESIZE; - bin = (Bins)pool->pagetable[pagenum]; - assert(bin <= B_PAGE); - size = binsize[bin]; - assert(((unsigned)p & (size - 1)) == 0); - -#if PTRCHECK >= 2 - if (bin < B_PAGE) - { - // Check that p is not on a free list - List *list; - - for (list = gcx->bucket[bin]; list; list = list->next) - { - assert((void *)list != p); - } - } -#endif -#endif - } -} - -void GC::error() -{ int i = 0; - - assert(i); -} - -void GC::addRoot(void *p) -{ - thread_invariant(gcx); - gcx->addRoot(p); -} - -void GC::removeRoot(void *p) -{ - gcx->removeRoot(p); -} - -void GC::addRange(void *pbot, void *ptop) -{ - thread_invariant(gcx); - gcx->addRange(pbot, ptop); -} - -void GC::removeRange(void *pbot) -{ - gcx->removeRange(pbot); -} - -void GC::fullcollect() -{ - thread_invariant(gcx); - - gcx->fullcollectshell(); - -#if 0 - { - GCStats stats; - - getStats(&stats); - WPRINTF(L"poolsize = %x, usedsize = %x, freelistsize = %x\n", - stats.poolsize, stats.usedsize, stats.freelistsize); - } -#endif - - gcx->log_collect(); -} - -void GC::fullcollectNoStack() -{ - gcx->noStack++; - fullcollect(); - gcx->noStack--; -} - -void GC::gencollect() -{ - gcx->fullcollectshell(); -} - -void GC::minimize() -{ - // Not implemented, ignore -} - - -void GC::setFinalizer(void *p, GC_FINALIZER pFn) -{ - thread_invariant(gcx); - - gcx->finalizer = pFn; - gcx->doFinalize(p); -} - -/***************************************** - * Retrieve statistics about garbage collection. - * Useful for debugging and tuning. - */ - -void GC::getStats(GCStats *stats) -{ - unsigned psize = 0; - unsigned usize = 0; - unsigned flsize = 0; - - unsigned n; - unsigned bsize = 0; - -//WPRINTF(L"getStats()\n"); - memset(stats, 0, sizeof(*stats)); - for (n = 0; n < gcx->npools; n++) - { Pool *pool = gcx->pooltable[n]; - - psize += pool->ncommitted * PAGESIZE; - for (unsigned j = 0; j < pool->ncommitted; j++) - { - Bins bin = (Bins)pool->pagetable[j]; - if (bin == B_FREE) - stats->freeblocks++; - else if (bin == B_PAGE) - stats->pageblocks++; - else if (bin < B_PAGE) - bsize += PAGESIZE; - } - } - - for (n = 0; n < B_PAGE; n++) - { -//WPRINTF(L"bin %d\n", n); - for (List *list = gcx->bucket[n]; list; list = list->next) - { -//WPRINTF(L"\tlist %x\n", list); - flsize += binsize[n]; - } - } - - usize = bsize - flsize; - - stats->poolsize = psize; - stats->usedsize = bsize - flsize; - stats->freelistsize = flsize; -} - -/* ============================ Gcx =============================== */ - -Gcx::Gcx() -{ int dummy; - - memset(this, 0, sizeof(Gcx)); - stackBottom = (char *)&dummy; - log_init(); -#if THREADINVARIANT - self = pthread_self(); -#endif - invariant(); -} - -Gcx::~Gcx() -{ - invariant(); - - for (unsigned i = 0; i < npools; i++) - { Pool *pool = pooltable[i]; - - delete pool; - } - if (pooltable) - ::free(pooltable); - - if (roots) - ::free(roots); - - if (ranges) - ::free(ranges); -} - -void Gcx::invariant() -{ -#if INVARIANT - unsigned i; - - thread_invariant(this); // assure we're called on the right thread - for (i = 0; i < npools; i++) - { Pool *pool = pooltable[i]; - - pool->invariant(); - if (i == 0) - { - assert(minAddr == pool->baseAddr); - } - if (i + 1 < npools) - { - assert(pool->cmp(pooltable[i + 1]) < 0); - } - else if (i + 1 == npools) - { - assert(maxAddr == pool->topAddr); - } - } - - if (roots) - { - assert(rootdim != 0); - assert(nroots <= rootdim); - } - - if (ranges) - { - assert(rangedim != 0); - assert(nranges <= rangedim); - - for (i = 0; i < nranges; i++) - { - assert(ranges[i].pbot); - assert(ranges[i].ptop); - assert(ranges[i].pbot <= ranges[i].ptop); - } - } - - for (i = 0; i < B_PAGE; i++) - { - for (List *list = bucket[i]; list; list = list->next) - { - } - } -#endif -} - -/*************************************** - */ - -void Gcx::addRoot(void *p) -{ - if (nroots == rootdim) - { - unsigned newdim = rootdim * 2 + 16; - void **newroots; - - newroots = (void **)::malloc(newdim * sizeof(newroots[0])); - assert(newroots); - if (roots) - { memcpy(newroots, roots, nroots * sizeof(newroots[0])); - ::free(roots); - } - roots = newroots; - rootdim = newdim; - } - roots[nroots] = p; - nroots++; -} - -void Gcx::removeRoot(void *p) -{ - unsigned i; - for (i = nroots; i--;) - { - if (roots[i] == p) - { - nroots--; - memmove(roots + i, roots + i + 1, (nroots - i) * sizeof(roots[0])); - return; - } - } - assert(zero); -} - - -/*************************************** - */ - -void Gcx::addRange(void *pbot, void *ptop) -{ - //WPRINTF(L"Thread %x ", pthread_self()); - //WPRINTF(L"%x->Gcx::addRange(%x, %x), nranges = %d\n", this, pbot, ptop, nranges); - if (nranges == rangedim) - { - unsigned newdim = rangedim * 2 + 16; - Range *newranges; - - newranges = (Range *)::malloc(newdim * sizeof(newranges[0])); - assert(newranges); - if (ranges) - { memcpy(newranges, ranges, nranges * sizeof(newranges[0])); - ::free(ranges); - } - ranges = newranges; - rangedim = newdim; - } - ranges[nranges].pbot = pbot; - ranges[nranges].ptop = ptop; - nranges++; -} - -void Gcx::removeRange(void *pbot) -{ - //WPRINTF(L"Thread %x ", pthread_self()); - //WPRINTF(L"%x->Gcx::removeRange(%x), nranges = %d\n", this, pbot, nranges); - for (unsigned i = nranges; i--;) - { - if (ranges[i].pbot == pbot) - { - nranges--; - memmove(ranges + i, ranges + i + 1, (nranges - i) * sizeof(ranges[0])); - return; - } - } - //WPRINTF(L"Wrong thread\n"); - - // This is a fatal error, but ignore it at Sun's request. - // The problem is that we can get a Close() call on a thread - // other than the one the range was allocated on. - //assert(zero); -} - - -/*********************************** - * Allocate a new pool with at least npages in it. - * Sort it into pooltable[]. - * Return NULL if failed. - */ - -Pool *Gcx::newPool(unsigned npages) -{ - Pool *pool; - Pool **newpooltable; - unsigned newnpools; - unsigned i; - - //WPRINTF(L"************Gcx::newPool(npages = %d)****************\n", npages); - - // Round up to COMMITSIZE pages - npages = (npages + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1); - - // Minimum of POOLSIZE - if (npages < POOLSIZE/PAGESIZE) - npages = POOLSIZE/PAGESIZE; - - // Allocate successively larger pools up to 8 megs - if (npools) - { unsigned n; - - n = npools; - if (n > 8) - n = 8; // cap pool size at 8 megs - n *= (POOLSIZE / PAGESIZE); - if (npages < n) - npages = n; - } - - pool = new Pool(npages); - if (pool) - { - if (!pool->baseAddr) - goto Lerr; - - newnpools = npools + 1; - newpooltable = (Pool **)::realloc(pooltable, newnpools * sizeof(Pool *)); - if (!newpooltable) - goto Lerr; - - // Sort pool into newpooltable[] - for (i = 0; i < npools; i++) - { - if (pool->cmp(newpooltable[i]) < 0) - break; - } - memmove(newpooltable + i + 1, newpooltable + i, (npools - i) * sizeof(Pool *)); - newpooltable[i] = pool; - - pooltable = newpooltable; - npools = newnpools; - - minAddr = pooltable[0]->baseAddr; - maxAddr = pooltable[npools - 1]->topAddr; - } - return pool; - - Lerr: - delete pool; - return NULL; -} - -/**************************************** - * Allocate a chunk of memory that is larger than a page. - * Return NULL if out of memory. - */ - -void *Gcx::bigAlloc(unsigned size) -{ - Pool *pool; - unsigned npages; - unsigned n; - unsigned pn; - unsigned freedpages; - void *p; - int state; - - npages = (size + PAGESIZE - 1) / PAGESIZE; - - for (state = 0; ; ) - { - for (n = 0; n < npools; n++) - { - pool = pooltable[n]; - pn = pool->allocPages(npages); - if (pn != ~0u) - goto L1; - } - - // Failed - switch (state) - { - case 0: - // Try collecting - freedpages = fullcollectshell(); - if (freedpages >= npools * ((POOLSIZE / PAGESIZE) / 4)) - { state = 1; - continue; - } - // Allocate new pool - pool = newPool(npages); - if (!pool) - { state = 2; - continue; - } - pn = pool->allocPages(npages); - assert(pn != ~0u); - goto L1; - - case 1: - // Allocate new pool - pool = newPool(npages); - if (!pool) - goto Lnomemory; - pn = pool->allocPages(npages); - assert(pn != ~0u); - goto L1; - - case 2: - goto Lnomemory; - } - } - - L1: - pool->pagetable[pn] = B_PAGE; - if (npages > 1) - memset(&pool->pagetable[pn + 1], B_PAGEPLUS, npages - 1); - p = pool->baseAddr + pn * PAGESIZE; - memset((char *)p + size, 0, npages * PAGESIZE - size); - #if MEMSTOMP - memset(p, 0xF1, size); - #endif - //printf("\tp = %x\n", p); - return p; - - Lnomemory: - assert(zero); - return NULL; -} - -/******************************* - * Allocate a page of bin's. - * Returns: - * 0 failed - */ - -int Gcx::allocPage(Bins bin) -{ - Pool *pool; - unsigned n; - unsigned pn; - char *p; - char *ptop; - - //printf("Gcx::allocPage(bin = %d)\n", bin); - for (n = 0; n < npools; n++) - { - pool = pooltable[n]; - pn = pool->allocPages(1); - if (pn != ~0u) - goto L1; - } - return 0; // failed - - L1: - pool->pagetable[pn] = (unsigned char)bin; - - // Convert page to free list - unsigned size = binsize[bin]; - List **b = &bucket[bin]; - - p = pool->baseAddr + pn * PAGESIZE; - ptop = p + PAGESIZE; - for (; p < ptop; p += size) - { - ((List *)p)->next = *b; - *b = (List *)p; - } - return 1; -} - -/******************************* - * Find Pool that pointer is in. - * Return NULL if not in a Pool. - * Assume pooltable[] is sorted. - */ - -Pool *Gcx::findPool(void *p) -{ - if (p >= minAddr && p < maxAddr) - { - if (npools == 1) - { - return pooltable[0]; - } - - for (unsigned i = 0; i < npools; i++) - { Pool *pool; - - pool = pooltable[i]; - if (p < pool->topAddr) - { if (pool->baseAddr <= p) - return pool; - break; - } - } - } - return NULL; -} - -/******************************* - * Find size of pointer p. - * Returns 0 if not a gc'd pointer - */ - -unsigned Gcx::findSize(void *p) -{ - Pool *pool; - unsigned size = 0; - - pool = findPool(p); - if (pool) - { - unsigned pagenum; - Bins bin; - - pagenum = ((unsigned)((char *)p - pool->baseAddr)) / PAGESIZE; - bin = (Bins)pool->pagetable[pagenum]; - size = binsize[bin]; - if (bin == B_PAGE) - { unsigned npages = pool->ncommitted; - unsigned char *pt; - unsigned i; - - pt = &pool->pagetable[0]; - for (i = pagenum + 1; i < npages; i++) - { - if (pt[i] != B_PAGEPLUS) - break; - } - size = (i - pagenum) * PAGESIZE; - } - } - return size; -} - - -/******************************* - * Compute bin for size. - */ - -Bins Gcx::findBin(unsigned size) -{ Bins bin; - - if (size <= 256) - { - if (size <= 64) - { - if (size <= 16) - bin = B_16; - else if (size <= 32) - bin = B_32; - else - bin = B_64; - } - else - { - if (size <= 128) - bin = B_128; - else - bin = B_256; - } - } - else - { - if (size <= 1024) - { - if (size <= 512) - bin = B_512; - else - bin = B_1024; - } - else - { - if (size <= 2048) - bin = B_2048; - else - bin = B_PAGE; - } - } - return bin; -} - -/************************************ - * Search a range of memory values and mark any pointers into the GC pool. - */ - -void Gcx::mark(void *pbot, void *ptop) -{ - void **p1 = (void **)pbot; - void **p2 = (void **)ptop; - unsigned changes = 0; - - //if (log) WPRINTF(L"Gcx::mark(%x .. %x)\n", pbot, ptop); - for (; p1 < p2; p1++) - { - Pool *pool; - char *p = (char *)(*p1); - - //if (log) WPRINTF(L"\tmark %x\n", p); - if (p >= minAddr) - { - pool = findPool(p); - if (pool) - { - unsigned offset = (unsigned)(p - pool->baseAddr); - unsigned bit; - unsigned pn = offset / PAGESIZE; - Bins bin = (Bins)pool->pagetable[pn]; - - //printf("\t\tfound pool %x, base=%x, pn = %d, bin = %d, bit = x%x\n", pool, pool->baseAddr, pn, bin, bit); - - // Adjust bit to be at start of allocated memory block - if (bin <= B_PAGE) - { - bit = (offset & notbinsize[bin]) >> 4; - //printf("\t\tbit = x%x\n", bit); - } - else if (bin == B_PAGEPLUS) - { - do - { --pn; - } while ((Bins)pool->pagetable[pn] == B_PAGEPLUS); - bit = pn * (PAGESIZE / 16); - } - else - { - // Don't mark bits in B_FREE or B_UNCOMMITTED pages - continue; - } - - //printf("\t\tmark(x%x) = %d\n", bit, pool->mark.test(bit)); - if (!pool->mark.test(bit)) - { - //if (log) WPRINTF(L"\t\tmarking %x\n", p); - pool->mark.set(bit); - pool->scan.set(bit); - changes = 1; - log_parent(sentinel_add(pool->baseAddr + bit * 16), sentinel_add(pbot)); - } - } - } - } - anychanges |= changes; -} - -/********************************* - * Return number of full pages free'd. - */ - -unsigned Gcx::fullcollectshell() -{ -#if __GCC__ - asm("pushl %eax"); - asm("pushl %ebx"); - asm("pushl %ecx"); - asm("pushl %edx"); - asm("pushl %ebp"); - asm("pushl %esi"); - asm("pushl %edi"); - // This function must ensure that all register variables are on the stack - // before &dummy - unsigned dummy; - dummy = fullcollect(&dummy - 7); - asm("addl $28,%esp"); -#elif _MSC_VER - __asm push eax; - __asm push ebx; - __asm push ecx; - __asm push edx; - __asm push ebp; - __asm push esi; - __asm push edi; - // This function must ensure that all register variables are on the stack - // before &dummy - unsigned dummy; - dummy = fullcollect(&dummy - 7); - __asm add esp,28; -#else - // This function must ensure that all register variables are on the stack - // before &dummy - unsigned dummy; - dummy = fullcollect(&dummy); -#endif - return dummy; -} - -unsigned Gcx::fullcollect(void *stackTop) -{ - unsigned n; - Pool *pool; - - //WPRINTF(L"Gcx::fullcollect()\n"); - invariant(); - anychanges = 0; - for (n = 0; n < npools; n++) - { - pool = pooltable[n]; - pool->mark.zero(); - pool->scan.zero(); - pool->freebits.zero(); - } - - // Mark each free entry, so it doesn't get scanned - for (n = 0; n < B_PAGE; n++) - { - for (List *list = bucket[n]; list; list = list->next) - { - pool = findPool(list); - assert(pool); - pool->freebits.set((unsigned)((char *)list - pool->baseAddr) / 16); - } - } - - for (n = 0; n < npools; n++) - { - pool = pooltable[n]; - pool->mark.copy(&pool->freebits); - } - - if (!noStack) - { - // Scan stack - //WPRINTF(L"scan stack bot = %x, top = %x\n", stackTop, stackBottom); -#if STACKGROWSDOWN - mark(stackTop, stackBottom); -#else - mark(stackBottom, stackTop); -#endif - } - - // Scan roots[] - //WPRINTF(L"scan roots[]\n"); - mark(roots, roots + nroots); - - // Scan ranges[] - //WPRINTF(L"scan ranges[]\n"); - //log++; - for (n = 0; n < nranges; n++) - { - //WPRINTF(L"\t%x .. %x\n", ranges[n].pbot, ranges[n].ptop); - mark(ranges[n].pbot, ranges[n].ptop); - } - //log--; - - //WPRINTF(L"\tscan heap\n"); - while (anychanges) - { - anychanges = 0; - for (n = 0; n < npools; n++) - { - unsigned *bbase; - unsigned *b; - unsigned *btop; - - pool = pooltable[n]; - - bbase = pool->scan.base(); - btop = bbase + pool->scan.nwords; - for (b = bbase; b < btop;) - { Bins bin; - unsigned pn; - unsigned u; - unsigned bitm; - char *o; - - bitm = *b; - if (!bitm) - { b++; - continue; - } - *b = 0; - - o = pool->baseAddr + (b - bbase) * 32 * 16; - if (!(bitm & 0xFFFF)) - { - bitm >>= 16; - o += 16 * 16; - } - for (; bitm; o += 16, bitm >>= 1) - { - if (!(bitm & 1)) - continue; - - pn = (o - pool->baseAddr) / PAGESIZE; - bin = (Bins)pool->pagetable[pn]; - if (bin < B_PAGE) - { - mark(o, o + binsize[bin]); - } - else if (bin == B_PAGE || bin == B_PAGEPLUS) - { - if (bin == B_PAGEPLUS) - { - while (pool->pagetable[pn - 1] != B_PAGE) - pn--; - } - u = 1; - while (pn + u < pool->ncommitted && pool->pagetable[pn + u] == B_PAGEPLUS) - u++; - mark(o, o + u * PAGESIZE); - } - } - } - } - } - - // Free up everything not marked - //WPRINTF(L"\tfree'ing\n"); - unsigned freedpages = 0; - unsigned freed = 0; - for (n = 0; n < npools; n++) - { unsigned pn; - unsigned ncommitted; - unsigned *bbase; - - pool = pooltable[n]; - bbase = pool->mark.base(); - ncommitted = pool->ncommitted; - for (pn = 0; pn < ncommitted; pn++, bbase += PAGESIZE / (32 * 16)) - { - Bins bin = (Bins)pool->pagetable[pn]; - - if (bin < B_PAGE) - { char *p; - char *ptop; - unsigned bit; - unsigned bitstride; - unsigned size = binsize[bin]; - - p = pool->baseAddr + pn * PAGESIZE; - ptop = p + PAGESIZE; - bit = pn * (PAGESIZE/16); - bitstride = size / 16; - -#if 0 // BUG: doesn't work because freebits() must also be cleared - // If free'd entire page - if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 && bbase[3] == 0 && - bbase[4] == 0 && bbase[5] == 0 && bbase[6] == 0 && bbase[7] == 0) - { - for (; p < ptop; p += size, bit += bitstride) - { - if (finalizer && pool->finals.nbits && - pool->finals.testClear(bit)) - { - (*finalizer)((List *)sentinel_add(p), NULL); - } - - List *list = (List *)p; - //printf("\tcollecting %x\n", list); - log_free(sentinel_add(list)); - - #if MEMSTOMP - memset(p, 0xF3, size); - #endif - } - pool->pagetable[pn] = B_FREE; - freed += PAGESIZE; - //printf("freeing entire page %d\n", pn); - continue; - } -#endif - for (; p < ptop; p += size, bit += bitstride) - { - if (!pool->mark.test(bit)) - { - sentinel_invariant(sentinel_add(p)); - - pool->freebits.set(bit); - if (finalizer && pool->finals.nbits && - pool->finals.testClear(bit)) - { - (*finalizer)((List *)sentinel_add(p), NULL); - } - - List *list = (List *)p; - //printf("\tcollecting %x\n", list); - log_free(sentinel_add(list)); - - #if MEMSTOMP - memset(p, 0xF3, size); - #endif - - freed += size; - } - } - } - else if (bin == B_PAGE) - { unsigned bit = pn * (PAGESIZE / 16); - - if (!pool->mark.test(bit)) - { char *p = pool->baseAddr + pn * PAGESIZE; - - sentinel_invariant(sentinel_add(p)); - if (finalizer && pool->finals.nbits && - pool->finals.testClear(bit)) - { - (*finalizer)(sentinel_add(p), NULL); - } - - //printf("\tcollecting big %x\n", p); - log_free(sentinel_add(p)); - pool->pagetable[pn] = B_FREE; - freedpages++; - #if MEMSTOMP - memset(p, 0xF3, PAGESIZE); - #endif - while (pn + 1 < ncommitted && pool->pagetable[pn + 1] == B_PAGEPLUS) - { - pn++; - pool->pagetable[pn] = B_FREE; - freedpages++; - - #if MEMSTOMP - p += PAGESIZE; - memset(p, 0xF3, PAGESIZE); - #endif - } - } - } - } - } - - // Zero buckets - memset(bucket, 0, sizeof(bucket)); - - // Free complete pages, rebuild free list - //WPRINTF(L"\tfree complete pages\n"); - unsigned recoveredpages = 0; - for (n = 0; n < npools; n++) - { unsigned pn; - unsigned ncommitted; - - pool = pooltable[n]; - ncommitted = pool->ncommitted; - for (pn = 0; pn < ncommitted; pn++) - { - Bins bin = (Bins)pool->pagetable[pn]; - unsigned bit; - unsigned u; - - if (bin < B_PAGE) - { - unsigned size = binsize[bin]; - unsigned bitstride = size / 16; - unsigned bitbase = pn * (PAGESIZE / 16); - unsigned bittop = bitbase + (PAGESIZE / 16); - char *p; - - bit = bitbase; - for (bit = bitbase; bit < bittop; bit += bitstride) - { if (!pool->freebits.test(bit)) - goto Lnotfree; - } - pool->pagetable[pn] = B_FREE; - recoveredpages++; - continue; - - Lnotfree: - p = pool->baseAddr + pn * PAGESIZE; - for (u = 0; u < PAGESIZE; u += size) - { bit = bitbase + u / 16; - if (pool->freebits.test(bit)) - { List *list; - - list = (List *)(p + u); - if (list->next != bucket[bin]) // avoid unnecessary writes - list->next = bucket[bin]; - bucket[bin] = list; - } - } - } - } - } - -#undef printf -// printf("recovered pages = %d\n", recoveredpages); -// printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, npools); -#define printf 1 || printf - - //WPRINTF(L"\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, npools); - invariant(); - - //WPRINTF(L"\tdone, freedpages = %d, recoveredpages = %d\n", freedpages, recoveredpages); - return freedpages + recoveredpages; -} - -/********************************* - * Run finalizer on p when it is free'd. - */ - -void Gcx::doFinalize(void *p) -{ - Pool *pool = findPool(p); - assert(pool); - - // Only allocate finals[] if we actually need it - if (!pool->finals.nbits) - pool->finals.alloc(pool->mark.nbits); - - pool->finals.set(((char *)p - pool->baseAddr) / 16); -} - -/* ============================ Pool =============================== */ - -Pool::Pool(unsigned npages) -{ - unsigned poolsize; - - //printf("Pool::Pool(%u)\n", npages); - poolsize = npages * PAGESIZE; - assert(poolsize >= POOLSIZE); - baseAddr = (char *)os_mem_map(poolsize); - - if (!baseAddr) - { - //WPRINTF(L"GC fail: poolsize = x%x, errno = %d\n", poolsize, errno); -#if USEROOT - PRINTF("message = '%s'\n", sys_errlist[errno]); -#else - //printf("message = '%s'\n", sys_errlist[errno]); -#endif - npages = 0; - poolsize = 0; - } - //assert(baseAddr); - topAddr = baseAddr + poolsize; - - mark.alloc(poolsize / 16); - scan.alloc(poolsize / 16); - freebits.alloc(poolsize / 16); - - pagetable = (unsigned char *)::malloc(npages); - memset(pagetable, B_UNCOMMITTED, npages); - - this->npages = npages; - ncommitted = 0; - - invariant(); -} - -Pool::~Pool() -{ - invariant(); - if (baseAddr) - { - int result; - - if (ncommitted) - { - result = os_mem_decommit(baseAddr, 0, ncommitted * PAGESIZE); - assert(result == 0); - } - - if (npages) - { - result = os_mem_unmap(baseAddr, npages * PAGESIZE); - assert(result == 0); - } - } - if (pagetable) - ::free(pagetable); -} - -void Pool::invariant() -{ -#if INVARIANT - mark.invariant(); - scan.invariant(); - - assert(baseAddr < topAddr); - assert(baseAddr + npages * PAGESIZE == topAddr); - assert(ncommitted <= npages); - - for (unsigned i = 0; i < npages; i++) - { Bins bin = (Bins)pagetable[i]; - - assert(bin < B_MAX); -#if 0 - // Buggy GCC doesn't compile this right with -O - if (i < ncommitted) - assert(bin != B_UNCOMMITTED); - else - assert(bin == B_UNCOMMITTED); -#endif - } -#endif -} - -/*************************** - * Used for sorting pooltable[] - */ - -int Pool::cmp(Pool *p2) -{ - return baseAddr - p2->baseAddr; -} - -/************************************** - * Allocate n pages from Pool. - * Returns ~0u on failure. - */ - -unsigned Pool::allocPages(unsigned n) -{ - unsigned i; - unsigned n2; - - //printf("Pool::allocPages(n = %d)\n", n); - n2 = n; - for (i = 0; i < ncommitted; i++) - { - if (pagetable[i] == B_FREE) - { - if (--n2 == 0) - { //printf("\texisting pn = %d\n", i - n + 1); - return i - n + 1; - } - } - else - n2 = n; - } - if (ncommitted + n < npages) - { - unsigned tocommit; - - tocommit = (n + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1); - if (ncommitted + tocommit > npages) - tocommit = npages - ncommitted; - //printf("\tlooking to commit %d more pages\n", tocommit); - //fflush(stdout); - if (os_mem_commit(baseAddr, ncommitted * PAGESIZE, tocommit * PAGESIZE) == 0) - { - memset(pagetable + ncommitted, B_FREE, tocommit); - i = ncommitted; - ncommitted += tocommit; - - while (i && pagetable[i - 1] == B_FREE) - i--; - - return i; - } - //printf("\tfailed to commit %d pages\n", tocommit); - } - - return ~0u; -} - -/********************************** - * Free npages pages starting with pagenum. - */ - -void Pool::freePages(unsigned pagenum, unsigned npages) -{ - memset(&pagetable[pagenum], B_FREE, npages); -} - -/* ======================= Leak Detector =========================== */ - -#if LOGGING - -struct Log -{ - void *p; - unsigned size; - unsigned line; - char *file; - void *parent; - - void print(); -}; - -void Log::print() -{ - WPRINTF(L" p = %x, size = %d, parent = %x ", p, size, parent); - if (file) - { - PRINTF("%s(%u)", file, line); - } - WPRINTF(L"\n"); -} - -LogArray::LogArray() -{ - data = NULL; - dim = 0; - allocdim = 0; -} - -LogArray::~LogArray() -{ - if (data) - ::free(data); - data = NULL; -} - -void LogArray::reserve(unsigned nentries) -{ - assert(dim <= allocdim); - if (allocdim - dim < nentries) - { - allocdim = (dim + nentries) * 2; - assert(dim + nentries <= allocdim); - if (!data) - { - data = (Log *)::malloc(allocdim * sizeof(*data)); - } - else - { Log *newdata; - - newdata = (Log *)::malloc(allocdim * sizeof(*data)); - assert(newdata); - memcpy(newdata, data, dim * sizeof(Log)); - ::free(data); - data = newdata; - } - assert(!allocdim || data); - } -} - -void LogArray::push(Log log) -{ - reserve(1); - data[dim++] = log; -} - -void LogArray::remove(unsigned i) -{ - memmove(data + i, data + i + 1, (dim - i) * sizeof(data[0])); - dim--; -} - -unsigned LogArray::find(void *p) -{ - for (unsigned i = 0; i < dim; i++) - { - if (data[i].p == p) - return i; - } - return ~0u; // not found -} - -void LogArray::copy(LogArray *from) -{ - reserve(from->dim - dim); - assert(from->dim <= allocdim); - memcpy(data, from->data, from->dim * sizeof(data[0])); - dim = from->dim; -} - - -/****************************/ - -void Gcx::log_init() -{ - //WPRINTF(L"+log_init()\n"); - current.reserve(1000); - prev.reserve(1000); - //WPRINTF(L"-log_init()\n"); -} - -void Gcx::log_parent(void *p, void *parent) -{ - //WPRINTF(L"+log_parent()\n"); - unsigned i; - - i = current.find(p); - if (i == ~0u) - { - WPRINTF(L"parent'ing unallocated memory %x, parent = %x\n", p, parent); - Pool *pool; - pool = findPool(p); - assert(pool); - unsigned offset = (unsigned)((char *)p - pool->baseAddr); - unsigned bit; - unsigned pn = offset / PAGESIZE; - Bins bin = (Bins)pool->pagetable[pn]; - bit = (offset & notbinsize[bin]); - WPRINTF(L"\tbin = %d, offset = x%x, bit = x%x\n", bin, offset, bit); - } - else - { - current.data[i].parent = parent; - } - //WPRINTF(L"-log_parent()\n"); -} - -void Gcx::log_malloc(void *p, unsigned size) -{ - //WPRINTF(L"+log_malloc(p = %x, size = %d)\n", p, size); - Log log; - - log.p = p; - log.size = size; - log.line = GC::line; - log.file = GC::file; - log.parent = NULL; - - GC::line = 0; - GC::file = NULL; - - current.push(log); - //WPRINTF(L"-log_malloc()\n"); -} - -void Gcx::log_free(void *p) -{ - //WPRINTF(L"+log_free(%x)\n", p); - unsigned i; - - i = current.find(p); - if (i == ~0u) - { - WPRINTF(L"free'ing unallocated memory %x\n", p); - } - else - current.remove(i); - //WPRINTF(L"-log_free()\n"); -} - -void Gcx::log_collect() -{ - //WPRINTF(L"+log_collect()\n"); - // Print everything in current that is not in prev - - WPRINTF(L"New pointers this cycle: --------------------------------\n"); - int used = 0; - for (unsigned i = 0; i < current.dim; i++) - { - unsigned j; - - j = prev.find(current.data[i].p); - if (j == ~0u) - current.data[i].print(); - else - used++; - } - - WPRINTF(L"All roots this cycle: --------------------------------\n"); - for (unsigned i = 0; i < current.dim; i++) - { - void *p; - unsigned j; - - p = current.data[i].p; - if (!findPool(current.data[i].parent)) - { - j = prev.find(current.data[i].p); - if (j == ~0u) - WPRINTF(L"N"); - else - WPRINTF(L" ");; - current.data[i].print(); - } - } - - WPRINTF(L"Used = %d-------------------------------------------------\n", used); - prev.copy(¤t); - - WPRINTF(L"-log_collect()\n"); -} - -#endif diff --git a/gc/gc.h b/gc/gc.h deleted file mode 100644 index 08ca5fc78..000000000 --- a/gc/gc.h +++ /dev/null @@ -1,62 +0,0 @@ - -#ifndef GC_H -#define GC_H - -struct Gcx; // private data - -typedef void (*GC_FINALIZER)(void *p, void *dummy); - -struct GCStats -{ - unsigned poolsize; // total size of pool - unsigned usedsize; // bytes allocated - unsigned freeblocks; // number of blocks marked FREE - unsigned freelistsize; // total of memory on free lists - unsigned pageblocks; // number of blocks marked PAGE -}; - -struct GC -{ - // For passing to debug code - static unsigned line; - static char *file; -// #define GC_LOG() ((GC::line = __LINE__), (GC::file = __FILE__)) - #define GC_LOG() ((void)0) - - Gcx *gcx; // implementation - - GC(); - ~GC(); - - void init(); - - char *strdup(const char *s); - void *malloc(size_t size); - void *calloc(size_t size, size_t n); - void *realloc(void *p, size_t size); - void free(void *p); - void *mallocdup(void *o, size_t size); - void check(void *p); - void error(); - - void setStackBottom(void *p); - void scanStaticData(); - - void addRoot(void *p); // add p to list of roots - void removeRoot(void *p); // remove p from list of roots - - void addRange(void *pbot, void *ptop); // add range to scan for roots - void removeRange(void *pbot); // remove range - - void fullcollect(); // do full garbage collection - void fullcollectNoStack(); // do full garbage collection; no scan stack - void gencollect(); // do generational garbage collection - void minimize(); // minimize physical memory usage - - void setFinalizer(void *p, GC_FINALIZER pFn); - - void getStats(GCStats *stats); -}; - -#endif - diff --git a/gc/linux.c b/gc/linux.c deleted file mode 100644 index 62bc585ed..000000000 --- a/gc/linux.c +++ /dev/null @@ -1,103 +0,0 @@ - -// Copyright (C) 2001 by Digital Mars -// All Rights Reserved -// Written by Walter Bright -// licensed to Chromium Communications - -#include -#include -#include - - -/************************************* - * This is all necessary to get fd initialized at startup. - */ - -#define FDMAP 0 - -#if FDMAP -#include - -struct OS_INIT -{ - static int fd; - - OS_INIT(); -}; - -OS_INIT os_init; - -int OS_INIT::fd = 0; - -OS_INIT::OS_INIT() -{ - fd = open("/dev/zero", O_RDONLY); -} -#endif - -/*********************************** - * Map memory. - */ - -void *os_mem_map(unsigned nbytes) -{ void *p; - - errno = 0; -#if FDMAP - p = mmap(NULL, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE, OS_INIT::fd, 0); -#else - p = mmap(NULL, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); -#endif - return (p == MAP_FAILED) ? NULL : p; -} - -/*********************************** - * Commit memory. - * Returns: - * 0 success - * !=0 failure - */ - -int os_mem_commit(void *base, unsigned offset, unsigned nbytes) -{ - return 0; -} - - -/*********************************** - * Decommit memory. - * Returns: - * 0 success - * !=0 failure - */ - -int os_mem_decommit(void *base, unsigned offset, unsigned nbytes) -{ - return 0; -} - -/*********************************** - * Unmap memory allocated with os_mem_map(). - * Returns: - * 0 success - * !=0 failure - */ - -int os_mem_unmap(void *base, unsigned nbytes) -{ - return munmap(base, nbytes); -} - - - - -/********************************************** - * Determine "bottom" of stack (actually the top on x86 systems). - */ - -void *os_query_stackBottom() -{ - int x; - - return (void *)(&x + 4); -} diff --git a/gc/linux.mak b/gc/linux.mak deleted file mode 100644 index a5a5af0ec..000000000 --- a/gc/linux.mak +++ /dev/null @@ -1,53 +0,0 @@ -## -## Linux makefile for dscript "dmgc" -## -## Copyright (C) 2001 Digital Mars -## All Rights Reserved -## - -CC= g++ - -CHILICOM_DIR= ../../chilicom_sdk - -CHILI_FLAGS= \ - -D_XOPEN_SOURCE=500 -D_XOPEN_SOURCE_EXTENDED=1 -D_BSD_SOURCE=1 \ - -D_POSIX_C_SOURCE=199903 -D_POSIX_PTHREAD_SEMANTICS=1 \ - -DEVENTLOG_ASSERTIONS -D_WIN32 - -#OPT=-g -OPT=-O2 -w - -FLAGS= -DUNICODE $(CHILI_FLAGS) -I$(CHILICOM_DIR)/include -L$(CHILICOM_DIR)/lib/linux2_debug - -CFLAGS= \ - $(OPT) -fPIC -Wall -Wno-non-virtual-dtor \ - -D_GNU_SOURCE -D_THREAD_SAFE -D_REENTRANT=1 \ - $(FLAGS) - -OBJS= gc.o bits.o linux.o - -default: dmgc.a testgc - -tests: testgc - -dmgc.a : $(OBJS) - ar -r $@ $(OBJS) - -testgc : dmgc.a testgc.o - $(CC) -o $@ $(CFLAGS) testgc.o dmgc.a $(LIBS) - -bits.o: bits.h bits.c -gc.o: os.h bits.h gc.h gc.c -linux.o: os.h linux.c - -clean: - -rm -f *.a - -rm -f *.o - -rm -f testgc - -.cpp.o : - $(CC) -c $(CFLAGS) $< - -.c.o : - $(CC) -c $(CFLAGS) $< - diff --git a/gc/makefile b/gc/makefile deleted file mode 100644 index d12cdebe8..000000000 --- a/gc/makefile +++ /dev/null @@ -1,51 +0,0 @@ - - -# Build library with Digital Mars C++ -# Copyright (c) 2000-2001 by Digital Mars -# written by Walter Bright -# www.digitalmars.com - -#DCHAR=-DUNICODE -DCHAR= - -#CFLAGS=-g -cpp $(DCHAR) -CFLAGS=-o -gl -cpp -6 $(DCHAR) -CC=sc - -.c.obj: - $(CC) -c $(CFLAGS) $* - -targets : dmgc.lib testgc.exe - -OBJS= gc.obj bits.obj win32.obj - -SRC1= gc.h gc.c bits.h bits.c os.h win32.c linux.c win32.mak msvc.mak -SRC2= -SRC3= -SRC4= -SRC5= - -dmgc.lib : $(OBJS) win32.mak - del dmgc.lib - lib dmgc /c/noi +gc+bits+win32; - -bits.obj: bits.h bits.c -gc.obj: os.h bits.h gc.h gc.c -win32.obj: os.h win32.c - -testgc.obj: gc.h testgc.c - -testgc.exe : win32.mak dmgc.lib testgc.obj - $(CC) -o testgc $(CFLAGS) testgc.obj dmgc.lib - -clean : - del $(OBJS) - -zip : $(SRC1) - zip32 -u dmgc $(SRC1) -# zip32 -u dmgc $(SRC2) -# zip32 -u dmgc $(SRC3) -# zip32 -u dmgc $(SRC4) -# zip32 -u dmgc $(SRC5) - - diff --git a/gc/msvc.mak b/gc/msvc.mak deleted file mode 100644 index a819d8239..000000000 --- a/gc/msvc.mak +++ /dev/null @@ -1,97 +0,0 @@ -!IF !DEFINED(NULL) -!IF DEFINED(OS) && "$(OS)" == "Windows_NT" -NULL= -!ELSE -NULL="NUL" -!ENDIF -!ENDIF - -CC=cl - -DCHAR=-DUNICODE -#DCHAR= - -CPPFLAGS= $(CPPFLAGS) $(DCHAR) /W4 /GR /Gy /TP /Fd$(OUTDIR)\dmgcvc.pdb -LFLAGS=/DEBUG /DEBUGTYPE:CV /PDBTYPE:CON -LIBFLAGS= - -# Debug settings -!IF !DEFINED(BUILD_CONFIG) || "$(BUILD_CONFIG)" == "DEBUG" || "$(BUILD_CONFIG)" == "debug" -OUTDIR=Debug -DEFAULTLIBFLAG= /MDd -CPPFLAGS= $(CPPFLAGS) $(DEFAULTLIBFLAG) /D_DEBUG /DDEBUG /ZI - -!ELSE - -# Release settings -DEFAULTLIBFLAG= /MD -CPPFLAGS= $(CPPFLAGS) $(DEFAULTLIBFLAG) /Zi /Ox -OUTDIR=Release -LFLAGS=$(LFLAGS) /OPT:REF /OPT:ICF,2 - -!ENDIF - -!IF "$(GENERATE_COD_FILES)" == "1" -CPPFLAGS=$(CPPFLAGS) /FAcs /Fa$*.cod -!ENDIF - -.c{$(OUTDIR)}.obj: - $(CC) -c $(CPPFLAGS) /Fo$@ $< - -targets : $(OUTDIR) \ - $(OUTDIR)\dmgcvc.lib \ - $(OUTDIR)\testgc.exe - - -$(OUTDIR): - -@IF NOT EXIST "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" - -OBJS1= $(OUTDIR)\gc.obj $(OUTDIR)\bits.obj $(OUTDIR)\win32.obj -OBJS2= -OBJS3= -OBJS4= -OBJS5= - -OBJS= $(OBJS1) $(OBJS2) $(OBJS3) $(OBJS4) $(OBJS5) - -SRC1= gc.h gc.c bits.h bits.c os.h win32.c linux.c -SRC2= -SRC3= -SRC4= -SRC5= linux.mak win32.mak -SRC6= msvc.mak - -$(OUTDIR)\dmgcvc.lib : $(OBJS) msvc.mak $(OUTDIR)\_libcmd.rsp - lib /out:$@ @$(OUTDIR)\_libcmd.rsp - -del $(OUTDIR)\_libcmd.rsp - -$(OUTDIR)\_libcmd.rsp : msvc.mak - echo $(OBJS1) > $(OUTDIR)\_libcmd.rsp -# echo $(OBJS2) >> $(OUTDIR)\_libcmd.rsp -# echo $(OBJS3) >> $(OUTDIR)\_libcmd.rsp -# echo $(OBJS4) >> $(OUTDIR)\_libcmd.rsp -# echo $(OBJS5) >> $(OUTDIR)\_libcmd.rsp - -$(OUTDIR)\bits.obj: bits.h bits.c -$(OUTDIR)\gc.obj: os.h bits.h gc.h gc.c -$(OUTDIR)\win32.obj: os.h win32.c -$(OUTDIR)\testgc.obj: gc.h testgc.c - -$(OUTDIR)\testgc.exe : msvc.mak $(OUTDIR)\dmgcvc.lib $(OUTDIR)\testgc.obj - $(CC) $(DEFAULTLIBFLAG) $(OUTDIR)\testgc.obj /link $(OUTDIR)\dmgcvc.lib /out:$@ - -test : $(OUTDIR)\testgc.exe - testgc - -clean: - @echo Cleaning $(BUILD_CONFIG) - -@IF EXIST "$(OUTDIR)/$(NULL)" delnode /q "$(OUTDIR)" - -zip : $(SRC1) $(SRC2) $(SRC3) $(SRC4) $(SRC5) $(SRC6) - zip32 -u dmgc $(SRC1) - zip32 -u dmgc $(SRC2) - zip32 -u dmgc $(SRC3) - zip32 -u dmgc $(SRC4) - zip32 -u dmgc $(SRC5) - zip32 -u dmgc $(SRC6) - diff --git a/gc/os.h b/gc/os.h deleted file mode 100644 index 0d502b431..000000000 --- a/gc/os.h +++ /dev/null @@ -1,19 +0,0 @@ - -// OS specific routines - -void *os_mem_map(unsigned nbytes); -int os_mem_commit(void *base, unsigned offset, unsigned nbytes); -int os_mem_decommit(void *base, unsigned offset, unsigned nbytes); -int os_mem_unmap(void *base, unsigned nbytes); -void *os_query_stackBottom(); -void os_query_staticdataseg(void **base, unsigned *nbytes); - - -// Threading - -#if defined linux -#include -#else -typedef long pthread_t; -pthread_t pthread_self(); -#endif diff --git a/gc/win32.mak b/gc/win32.mak deleted file mode 100644 index d12cdebe8..000000000 --- a/gc/win32.mak +++ /dev/null @@ -1,51 +0,0 @@ - - -# Build library with Digital Mars C++ -# Copyright (c) 2000-2001 by Digital Mars -# written by Walter Bright -# www.digitalmars.com - -#DCHAR=-DUNICODE -DCHAR= - -#CFLAGS=-g -cpp $(DCHAR) -CFLAGS=-o -gl -cpp -6 $(DCHAR) -CC=sc - -.c.obj: - $(CC) -c $(CFLAGS) $* - -targets : dmgc.lib testgc.exe - -OBJS= gc.obj bits.obj win32.obj - -SRC1= gc.h gc.c bits.h bits.c os.h win32.c linux.c win32.mak msvc.mak -SRC2= -SRC3= -SRC4= -SRC5= - -dmgc.lib : $(OBJS) win32.mak - del dmgc.lib - lib dmgc /c/noi +gc+bits+win32; - -bits.obj: bits.h bits.c -gc.obj: os.h bits.h gc.h gc.c -win32.obj: os.h win32.c - -testgc.obj: gc.h testgc.c - -testgc.exe : win32.mak dmgc.lib testgc.obj - $(CC) -o testgc $(CFLAGS) testgc.obj dmgc.lib - -clean : - del $(OBJS) - -zip : $(SRC1) - zip32 -u dmgc $(SRC1) -# zip32 -u dmgc $(SRC2) -# zip32 -u dmgc $(SRC3) -# zip32 -u dmgc $(SRC4) -# zip32 -u dmgc $(SRC5) - - diff --git a/gc2/arraysetlength.d b/gc2/arraysetlength.d new file mode 100644 index 000000000..8eaa322ac --- /dev/null +++ b/gc2/arraysetlength.d @@ -0,0 +1,93 @@ + +// D Language Runtime Library +// Copyright (c) 2001 by Digital Mars +// All Rights Reserved +// www.digitalmars.com + +// Do array resizing. + +import gc; + +extern GC gc; + +struct Array +{ + uint length; + byte* data; +} + + +/****************************** + * Resize dynamic arrays other than bit[]. + */ + +Array __d_arraysetlength(uint newlength, uint sizeelem, Array *p) +{ + byte* newdata; + uint newsize; + + //printf("p = %p, sizeelem = %d, newlength = %d\n", p, sizeelem, newlength); + + assert(sizeelem); + assert((p.data && p.length) || (!p.data && !p.length)); + if (newlength) + { + newsize = sizeelem * newlength; + newdata = (byte *)gc.malloc(newsize); + if (p.data) + { uint size; + + size = p.length * sizeelem; + if (newsize < size) + size = newsize; + else if (newsize > size) + newdata[size .. newsize] = 0; + newdata[0 .. size] = p.data[0 .. size]; + } + else + newdata[0 .. newsize] = 0; + } + else + { + newdata = null; + } + + p.data = newdata; + p.length = newlength; + return *p; +} + +/*************************** + * Resize bit[] arrays. + */ + +Array __d_arraysetlengthb(uint newlength, Array *p) +{ + byte* newdata; + uint newsize; + + //printf("p = %p, newlength = %d\n", p, newlength); + + assert((p.data && p.length) || (!p.data && !p.length)); + if (newlength) + { + newsize = (newlength + 31) >> 5; // # of uint + newdata = (byte *)gc.malloc(newsize * 4); + if (p.data) + { uint size; + + size = (p.length + 31) >> 5; // # of uint + if (newsize < size) + size = newsize; + newdata[0 .. size * 4] = p.data[0 .. size * 4]; + } + } + else + { + newdata = NULL; + } + + p.data = newdata; + p.length = newlength; + return *p; +} diff --git a/gc2/gc.d b/gc2/gc.d new file mode 100644 index 000000000..5207fdf96 --- /dev/null +++ b/gc2/gc.d @@ -0,0 +1,354 @@ +// +// Copyright (C) 2001-2002 by Digital Mars +// All Rights Reserved +// Written by Walter Bright +// www.digitalmars.com + + +// Storage allocation + +//debug = PRINTF; + +import c.stdlib; +import string; +import gcx; +import outofmemory; +import gcstats; + +GC* _gc; + +void addRoot(void *p) { _gc.addRoot(p); } +void removeRoot(void *p) { _gc.removeRoot(p); } +void addRange(void *pbot, void *ptop) { _gc.addRange(pbot, ptop); } +void removeRange(void *pbot) { _gc.removeRange(pbot); } +void fullCollect() { _gc.fullCollect(); } +void fullCollectNoStack() { _gc.fullCollectNoStack(); } +void genCollect() { _gc.genCollect(); } +void minimize() { _gc.minimize(); } +void disable() { _gc.disable(); } +void enable() { _gc.enable(); } +void getStats(out GCStats stats) { _gc.getStats(stats); } + + +extern (C) +{ + +void gc_init() +{ + _gc = (GC *) c.stdlib.calloc(1, GC.size); + _gc.init(); + //_gc.setStackBottom(_atopsp); + _gc.scanStaticData(); +} + +void gc_term() +{ + _gc.fullCollectNoStack(); +} + +Object _d_newclass(ClassInfo ci) +{ + void *p; + + debug(PRINTF) printf("_d_newclass(ci = %p)\n", ci); + if (ci.flags & 1) // if COM object + { + p = (Object)c.stdlib.malloc(ci.init.length); + if (!p) + _d_OutOfMemory(); + } + else + { + p = _gc.malloc(ci.init.length); + debug(PRINTF) printf(" p = %p\n", p); + _gc.setFinalizer(p, &new_finalizer); + } + + debug (PRINTF) + { + printf("p = %p\n", p); + printf("ci = %p, ci.init = %p, len = %d\n", ci, ci.init, ci.init.length); + printf("vptr = %p\n", *(void **)ci.init); + printf("vtbl[0] = %p\n", (*(void ***)ci.init)[0]); + printf("vtbl[1] = %p\n", (*(void ***)ci.init)[1]); + printf("init[0] = %x\n", ((uint *)ci.init)[0]); + printf("init[1] = %x\n", ((uint *)ci.init)[1]); + printf("init[2] = %x\n", ((uint *)ci.init)[2]); + printf("init[3] = %x\n", ((uint *)ci.init)[3]); + printf("init[4] = %x\n", ((uint *)ci.init)[4]); + } + + + // Initialize it + ((byte*)p)[0 .. ci.init.length] = ci.init[]; + + //printf("initialization done\n"); + return (Object)p; +} + +extern (D) alias void (*fp_t)(Object); // generic function pointer + +void _d_delclass(Object *p) +{ + if (*p) + { + version(none) + { + ClassInfo **pc = (ClassInfo **)*p; + if (*pc) + { + ClassInfo c = **pc; + + if (c.destructor) + { + fp_t fp = (fp_t)c.destructor; + (*fp)(*p); // call destructor + } + *pc = null; // zero vptr + } + } + _gc.free(*p); + *p = null; + } +} + +ulong _d_new(uint length, uint size) +{ + void *p; + ulong result; + + debug(PRINTF) printf("_d_new(length = %d, size = %d)\n", length, size); + if (length == 0 || size == 0) + result = 0; + else + { + p = _gc.malloc(length * size); + debug(PRINTF) printf(" p = %p\n", p); + memset(p, 0, length * size); + result = (ulong)length + ((ulong)(uint)p << 32); + } + return result; +} + +struct Array +{ + uint length; + byte *data; +}; + +// Perhaps we should get a a size argument like _d_new(), so we +// can zero out the array? + +void _d_delarray(Array *p) +{ + if (p) + { + assert(!p.length || p.data); + if (p.data) + _gc.free(p.data); + p.data = null; + p.length = 0; + } +} + + +} + +void new_finalizer(void *p, void *dummy) +{ + //printf("new_finalizer(p = %p)\n", p); + _d_callfinalizer(p); +} + +extern (C) +void _d_callfinalizer(void *p) +{ + //printf("_d_callfinalizer(p = %p)\n", p); + if (p) // not necessary if called from gc + { + ClassInfo **pc = (ClassInfo **)p; + if (*pc) + { + ClassInfo c = **pc; + + do + { + if (c.destructor) + { + fp_t fp = (fp_t)c.destructor; + (*fp)((Object)p); // call destructor + } + c = c.base; + } while (c); + *pc = null; // zero vptr + } + } +} + +/+ ------------------------------------------------ +/ + + +/****************************** + * Resize dynamic arrays other than bit[]. + */ + +extern (C) +byte[] _d_arraysetlength(uint newlength, uint sizeelem, Array *p) +{ + byte* newdata; + uint newsize; + + debug(PRINTF) + { + printf("_d_arraysetlength(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength); + if (p) + printf("\tp.data = %p, p.length = %d\n", p.data, p.length); + } + + assert(sizeelem); + assert(!p.length || p.data); + if (newlength) + { + newsize = sizeelem * newlength; + if (p.length) + { uint size = p.length * sizeelem; + + newdata = p.data; + if (newsize > size) + { + uint cap = _gc.capacity(p.data); + if (cap < newsize) + { + newdata = (byte *)_gc.malloc(newsize); + newdata[0 .. size] = p.data[0 .. size]; + } + newdata[size .. newsize] = 0; + } + } + else + { + newdata = (byte *)_gc.calloc(newsize, 1); + } + } + else + { + newdata = null; + } + + p.data = newdata; + p.length = newlength; + return newdata[0 .. newlength]; +} + +/*************************** + * Resize bit[] arrays. + */ + +extern (C) +bit[] _d_arraysetlengthb(uint newlength, Array *p) +{ + byte* newdata; + uint newsize; + + debug (PRINTF) + printf("p = %p, newlength = %d\n", p, newlength); + + assert(!p.length || p.data); + if (newlength) + { + newsize = ((newlength + 31) >> 5) * 4; // # bytes rounded up to uint + if (p.length) + { uint size = ((p.length + 31) >> 5) * 4; + + newdata = p.data; + if (newsize > size) + { + uint cap = _gc.capacity(p.data); + if (cap < newsize) + { + newdata = (byte *)_gc.malloc(newsize); + newdata[0 .. size] = p.data[0 .. size]; + } + newdata[size .. newsize] = 0; + } + } + else + { + newdata = (byte *)_gc.calloc(newsize, 1); + } + } + else + { + newdata = null; + } + + p.data = newdata; + p.length = newlength; + return ((bit *)newdata)[0 .. newlength]; +} + +/**************************************** + * Append y[] to array x[]. + * size is size of each array element. + */ + +extern (C) +Array _d_arrayappend(Array *px, byte[] y, uint size) +{ + + uint cap = _gc.capacity(px.data); + uint length = px.length; + uint newlength = length + y.length; + if (newlength * size > cap) + { byte* newdata; + + newdata = (byte *)_gc.malloc(newlength * size); + memcpy(newdata, px.data, length * size); + px.data = newdata; + } + px.length = newlength; + px.data[length * size .. newlength * size] = y[]; + return *px; +} + + +extern (C) +byte[] _d_arrayappendc(inout byte[] x, in uint size, ...) +{ + uint cap = _gc.capacity(x); + uint length = x.length; + uint newlength = length + 1; + if (newlength * size > cap) + { byte* newdata; + + newdata = (byte *)_gc.malloc(newlength * size); + memcpy(newdata, x, length * size); + ((void **)(&x))[1] = newdata; + } + byte *argp = (byte *)(&size + 1); + + *(int *)&x = newlength; + ((byte *)x)[length * size .. newlength * size] = argp[0 .. size]; + return x; + +/+ + byte[] a; + uint length; + void *argp; + + //printf("size = %d\n", size); + length = x.length + 1; + a = new byte[length * size]; + memcpy(a, x, x.length * size); + argp = &size + 1; + //printf("*argp = %llx\n", *(long *)argp); + memcpy(&a[x.length * size], argp, size); + //printf("a[0] = %llx\n", *(long *)&a[0]); + *(int *)&a = length; // jam length + //printf("a[0] = %llx\n", *(long *)&a[0]); + x = a; + return a; ++/ +} + + diff --git a/gc2/gcbits.d b/gc2/gcbits.d new file mode 100644 index 000000000..c00f0c0b9 --- /dev/null +++ b/gc2/gcbits.d @@ -0,0 +1,176 @@ + +// Copyright (C) 2001-2002 by Digital Mars +// All Rights Reserved +// www.digitalmars.com +// Written by Walter Bright + +import string; +import c.stdlib; +import outofmemory; +import intrinsic; + +//version = Asm86; +version = bitops; + +struct GCBits +{ + const int BITS_PER_WORD = 32; + const int BITS_SHIFT = 5; + const int BITS_MASK = 31; + + uint *data = null; + uint nwords = 0; // allocated words in data[] excluding sentinals + uint nbits = 0; // number of bits in data[] excluding sentinals + + void Dtor() + { + if (data) + { + free(data); + data = null; + } + } + + invariant + { + if (data) + { + assert(nwords * data[0].size * 8 >= nbits); + } + } + + void alloc(uint nbits) + { + this.nbits = nbits; + nwords = (nbits + (BITS_PER_WORD - 1)) >> BITS_SHIFT; + data = cast(uint *)calloc(nwords + 2, uint.size); + if (!data) + _d_OutOfMemory(); + } + + uint test(uint i) + in + { + assert(i < nbits); + } + body + { + return (cast(bit *)(data + 1))[i]; + //return data[1 + (i >> BITS_SHIFT)] & (1 << (i & BITS_MASK)); + } + + void set(uint i) + in + { + assert(i < nbits); + } + body + { + (cast(bit *)(data + 1))[i] = 1; + //data[1 + (i >> BITS_SHIFT)] |= (1 << (i & BITS_MASK)); + } + + void clear(uint i) + in + { + assert(i < nbits); + } + body + { + (cast(bit *)(data + 1))[i] = 0; + //data[1 + (i >> BITS_SHIFT)] &= ~(1 << (i & BITS_MASK)); + } + + uint testClear(uint i) + { + version (bitops) + { + return intrinsic.btr(data + 1, i); + } + else version (Asm86) + { + asm + { + naked ; + mov EAX,data[EAX] ; + mov ECX,i-4[ESP] ; + btr 4[EAX],ECX ; + sbb EAX,EAX ; + ret 4 ; + } + } + else + { uint result; + + result = (cast(bit *)(data + 1))[i]; + (cast(bit *)(data + 1))[i] = 0; + + //uint *p = &data[1 + (i >> BITS_SHIFT)]; + //uint mask = (1 << (i & BITS_MASK)); + //result = *p & mask; + //*p &= ~mask; + return result; + } + } + + void zero() + { + memset(data + 1, 0, nwords * uint.size); + } + + void copy(GCBits *f) + in + { + assert(nwords == f.nwords); + } + body + { + memcpy(data + 1, f.data + 1, nwords * uint.size); + } + + uint *base() + in + { + assert(data); + } + body + { + return data + 1; + } +} + +unittest +{ + GCBits b; + + b.alloc(786); + assert(b.test(123) == 0); + assert(b.testClear(123) == 0); + b.set(123); + assert(b.test(123) != 0); + assert(b.testClear(123) != 0); + assert(b.test(123) == 0); + + b.set(785); + b.set(0); + assert(b.test(785) != 0); + assert(b.test(0) != 0); + b.zero(); + assert(b.test(785) == 0); + assert(b.test(0) == 0); + + GCBits b2; + b2.alloc(786); + b2.set(38); + b.copy(&b2); + assert(b.test(38) != 0); + b2.Dtor(); + + b.Dtor(); +} + +/+ +void main() +{ +} ++/ diff --git a/gc2/gcx.d b/gc2/gcx.d new file mode 100644 index 000000000..0cc857cfe --- /dev/null +++ b/gc2/gcx.d @@ -0,0 +1,2011 @@ +// +// Copyright (C) 2001-2002 by Digital Mars +// All Rights Reserved +// Written by Walter Bright +// www.digitalmars.com + +// D Garbage Collector implementation + +/************** Debugging ***************************/ + +//debug = PRINTF; // turn on printf's +//debug = COLLECT_PRINTF; // turn on printf's +//debug = THREADINVARIANT; // check thread integrity +//debug = LOGGING; // log allocations / frees +//debug = MEMSTOMP; // stomp on memory +//debug = SENTINEL; // add underrun/overrrun protection +//debug = PTRCHECK; // more pointer checking +//debug = PTRCHECK2; // thorough but slow pointer checking + +/*************** Configuration *********************/ + +version = STACKGROWSDOWN; // growing the stack means subtracting from the stack pointer + // (use for Intel X86 CPUs) + // else growing the stack means adding to the stack pointer +version = MULTI_THREADED; // produce multithreaded version + +/***************************************************/ + + +debug (PRINTF) import c.stdio; + +import c.stdlib; +import gcbits; +import win32; +import outofmemory; +import gc; +import gcstats; + +version (MULTI_THREADED) +{ + import thread; +} + +/* ======================= Leak Detector =========================== */ + +debug (LOGGING) +{ + struct Log + { + void *p; + uint size; + uint line; + char *file; + void *parent; + + void print() + { + printf(" p = %x, size = %d, parent = %x ", p, size, parent); + if (file) + { + printf("%s(%u)", file, line); + } + printf("\n"); + } + } + + + struct LogArray + { + uint dim; + uint allocdim; + Log *data; + + void Dtor() + { + if (data) + c.stdlib.free(data); + data = null; + } + + void reserve(uint nentries) + { + assert(dim <= allocdim); + if (allocdim - dim < nentries) + { + allocdim = (dim + nentries) * 2; + assert(dim + nentries <= allocdim); + if (!data) + { + data = (Log *)c.stdlib.malloc(allocdim * Log.size); + } + else + { Log *newdata; + + newdata = (Log *)c.stdlib.malloc(allocdim * Log.size); + assert(newdata); + memcpy(newdata, data, dim * Log.size); + c.stdlib.free(data); + data = newdata; + } + assert(!allocdim || data); + } + } + + void push(Log log) + { + reserve(1); + data[dim++] = log; + } + + void remove(uint i) + { + memmove(data + i, data + i + 1, (dim - i) * Log.size); + dim--; + } + + uint find(void *p) + { + for (uint i = 0; i < dim; i++) + { + if (data[i].p == p) + return i; + } + return ~0u; // not found + } + + void copy(LogArray *from) + { + reserve(from.dim - dim); + assert(from.dim <= allocdim); + memcpy(data, from.data, from.dim * Log.size); + dim = from.dim; + } + + + } +} + +/* ============================ GC =============================== */ + + +alias int size_t; +alias void (*GC_FINALIZER)(void *p, void *dummy); + +class GCLock { } // just a dummy so we can get a global lock + +struct GC +{ + // For passing to debug code + static uint line; + static char *file; + + Gcx *gcx; // implementation + static ClassInfo gcLock; // global lock + + void init() + { + gcLock = GCLock.classinfo; + gcx = (Gcx *)c.stdlib.calloc(1, Gcx.size); + gcx.init(); + setStackBottom(win32.os_query_stackBottom()); + } + + + void Dtor() + { + version (linux) + { + //debug(PRINTF) printf("Thread %x ", pthread_self()); + //debug(PRINTF) printf("GC.Dtor()\n"); + } + + if (gcx) + { + gcx.Dtor(); + c.stdlib.free(gcx); + gcx = null; + } + } + + invariant + { + if (gcx) + gcx.thread_Invariant(); + } + + void *malloc(size_t size) + { void *p = null; + Bins bin; + + //debug(PRINTF) printf("GC::malloc(size = %d, gcx = %p)\n", size, gcx); + assert(gcx); + //debug(PRINTF) printf("gcx.self = %x, pthread_self() = %x\n", gcx.self, pthread_self()); + synchronized (gcLock) + { + if (size) + { + size += SENTINEL_EXTRA; + + // Compute size bin + bin = gcx.findBin(size); + + if (bin < B_PAGE) + { + p = gcx.bucket[bin]; + if (p == null) + { + if (!gcx.allocPage(bin)) // try to find a new page + { + if (!gcx.fullcollectshell()) // collect to find a new page + { + //gcx.newPool(1); + } + } + if (!gcx.bucket[bin] && !gcx.allocPage(bin)) + { int result; + + gcx.newPool(1); // allocate new pool to find a new page + result = gcx.allocPage(bin); + if (!result) + return null; + } + p = gcx.bucket[bin]; + } + + // Return next item from free list + gcx.bucket[bin] = ((List *)p).next; + memset(p + size, 0, binsize[bin] - size); + //debug(PRINTF) printf("\tmalloc => %x\n", p); + debug (MEMSTOMP) memset(p, 0xF0, size); + } + else + { + p = gcx.bigAlloc(size); + if (!p) + return null; + } + size -= SENTINEL_EXTRA; + p = sentinel_add(p); + sentinel_init(p, size); + gcx.log_malloc(p, size); + } + } + return p; + } + + + void *calloc(size_t size, size_t n) + { + uint len; + void *p; + + len = size * n; + p = malloc(len); + if (p) + { //debug(PRINTF) printf("calloc: %x len %d\n", p, len); + memset(p, 0, len); + } + return p; + } + + + void *realloc(void *p, size_t size) + { + if (!size) + { if (p) + { free(p); + p = null; + } + } + else if (!p) + { + p = malloc(size); + } + else + { void *p2; + uint psize; + + //debug(PRINTF) printf("GC::realloc(p = %x, size = %u)\n", p, size); + version (SENTINEL) + { + sentinel_Invariant(p); + psize = *sentinel_size(p); + if (psize != size) + { + p2 = malloc(size); + if (psize < size) + size = psize; + //debug(PRINTF) printf("\tcopying %d bytes\n",size); + memcpy(p2, p, size); + p = p2; + } + } + else + { + psize = gcx.findSize(p); // find allocated size + if (psize < size || // if new size is bigger + psize > size * 2) // or less than half + { + p2 = malloc(size); + if (psize < size) + size = psize; + //debug(PRINTF) printf("\tcopying %d bytes\n",size); + memcpy(p2, p, size); + p = p2; + } + } + } + return p; + } + + + void free(void *p) + { + Pool *pool; + uint pagenum; + Bins bin; + uint biti; + + if (!p) + return; + + // Find which page it is in + pool = gcx.findPool(p); + if (!pool) // if not one of ours + return; // ignore + sentinel_Invariant(p); + p = sentinel_sub(p); + pagenum = (p - pool.baseAddr) / PAGESIZE; + + synchronized (gcLock) + { + if (pool.finals.nbits && gcx.finalizer) + { + biti = (uint)(p - pool.baseAddr) / 16; + if (pool.finals.testClear(biti)) + { + (*gcx.finalizer)(sentinel_add(p), null); + } + } + + bin = (Bins)pool.pagetable[pagenum]; + if (bin == B_PAGE) // if large alloc + { int npages; + uint n; + + // Free pages + npages = 1; + n = pagenum; + while (++n < pool.ncommitted && pool.pagetable[n] == B_PAGEPLUS) + npages++; + debug (MEMSTOMP) memset(p, 0xF2, npages * PAGESIZE); + pool.freePages(pagenum, npages); + } + else + { // Add to free list + List *list = (List *)p; + + debug (MEMSTOMP) memset(p, 0xF2, binsize[bin]); + + list.next = gcx.bucket[bin]; + gcx.bucket[bin] = list; + } + } + gcx.log_free(sentinel_add(p)); + } + + + /**************************************** + * Determine the allocated size of pointer p. + * If p is an interior pointer or not a gc allocated pointer, + * return 0. + */ + + size_t capacity(void *p) + { + version (SENTINEL) + { + p = sentinel_sub(p); + size_t size = gcx.findSize(p); + + // Check for interior pointer + // This depends on: + // 1) size is a power of 2 for less than PAGESIZE values + // 2) base of memory pool is aligned on PAGESIZE boundary + if ((uint)p & (size - 1) & (PAGESIZE - 1)) + size = 0; + return size ? size - SENTINAL_EXTRA : 0; + } + else + { + if (p == gcx.p_cache) + return gcx.size_cache; + + size_t size = gcx.findSize(p); + + // Check for interior pointer + // This depends on: + // 1) size is a power of 2 for less than PAGESIZE values + // 2) base of memory pool is aligned on PAGESIZE boundary + if ((uint)p & (size - 1) & (PAGESIZE - 1)) + size = 0; + else + { + gcx.p_cache = p; + gcx.size_cache = size; + } + + return size; + } + } + + + /**************************************** + * Verify that pointer p: + * 1) belongs to this memory pool + * 2) points to the start of an allocated piece of memory + * 3) is not on a free list + */ + + void check(void *p) + { + if (p) + { + synchronized (gcLock) + { + sentinel_Invariant(p); + debug (PTRCHECK) + { + Pool *pool; + uint pagenum; + Bins bin; + uint size; + + p = sentinel_sub(p); + pool = gcx.findPool(p); + assert(pool); + pagenum = (p - pool.baseAddr) / PAGESIZE; + bin = (Bins)pool.pagetable[pagenum]; + assert(bin <= B_PAGE); + size = binsize[bin]; + assert(((uint)p & (size - 1)) == 0); + + debug (PTRCHECK2) + { + if (bin < B_PAGE) + { + // Check that p is not on a free list + List *list; + + for (list = gcx.bucket[bin]; list; list = list.next) + { + assert((void *)list != p); + } + } + } + } + } + } + } + + + void setStackBottom(void *p) + { + version (STACKGROWSDOWN) + { + //p = (void *)((uint *)p + 4); + if (p > gcx.stackBottom) + { + //debug(PRINTF) printf("setStackBottom(%x)\n", p); + gcx.stackBottom = p; + } + } + else + { + //p = (void *)((uint *)p - 4); + if (p < gcx.stackBottom) + { + //debug(PRINTF) printf("setStackBottom(%x)\n", p); + gcx.stackBottom = (char *)p; + } + } + } + + void scanStaticData() + { + void *pbot; + void *ptop; + uint nbytes; + + os_query_staticdataseg(&pbot, &nbytes); + ptop = pbot + nbytes; + addRange(pbot, ptop); + } + + + void addRoot(void *p) // add p to list of roots + { + synchronized (gcLock) + { + gcx.addRoot(p); + } + } + + void removeRoot(void *p) // remove p from list of roots + { + synchronized (gcLock) + { + gcx.removeRoot(p); + } + } + + void addRange(void *pbot, void *ptop) // add range to scan for roots + { + synchronized (gcLock) + { + gcx.addRange(pbot, ptop); + } + } + + void removeRange(void *pbot) // remove range + { + synchronized (gcLock) + { + gcx.removeRange(pbot); + } + } + + void fullCollect() // do full garbage collection + { + debug(PRINTF) printf("GC.fullCollect()\n"); + synchronized (gcLock) + { + gcx.fullcollectshell(); + } + + version (none) + { + GCStats stats; + + getStats(stats); + debug(PRINTF) printf("poolsize = %x, usedsize = %x, freelistsize = %x\n", + stats.poolsize, stats.usedsize, stats.freelistsize); + } + + gcx.log_collect(); + } + + void fullCollectNoStack() // do full garbage collection + { + gcx.noStack++; + fullCollect(); + gcx.noStack--; + } + + void genCollect() // do generational garbage collection + { + synchronized (gcLock) + { + gcx.fullcollectshell(); + } + } + + void minimize() // minimize physical memory usage + { + // Not implemented, ignore + } + + void setFinalizer(void *p, GC_FINALIZER pFn) + { + synchronized (gcLock) + { + gcx.finalizer = pFn; + gcx.doFinalize(p); + } + } + + void enable() + { + synchronized (gcLock) + { + assert(gcx.disabled > 0); + gcx.disabled--; + } + } + + void disable() + { + synchronized (gcLock) + { + gcx.disabled++; + } + } + + /***************************************** + * Retrieve statistics about garbage collection. + * Useful for debugging and tuning. + */ + + void getStats(out GCStats stats) + { + uint psize = 0; + uint usize = 0; + uint flsize = 0; + + uint n; + uint bsize = 0; + + //debug(PRINTF) printf("getStats()\n"); + memset(&stats, 0, GCStats.size); + + synchronized (gcLock) + { + for (n = 0; n < gcx.npools; n++) + { Pool *pool = gcx.pooltable[n]; + + psize += pool.ncommitted * PAGESIZE; + for (uint j = 0; j < pool.ncommitted; j++) + { + Bins bin = (Bins)pool.pagetable[j]; + if (bin == B_FREE) + stats.freeblocks++; + else if (bin == B_PAGE) + stats.pageblocks++; + else if (bin < B_PAGE) + bsize += PAGESIZE; + } + } + + for (n = 0; n < B_PAGE; n++) + { + //debug(PRINTF) printf("bin %d\n", n); + for (List *list = gcx.bucket[n]; list; list = list.next) + { + //debug(PRINTF) printf("\tlist %x\n", list); + flsize += binsize[n]; + } + } + } + + usize = bsize - flsize; + + stats.poolsize = psize; + stats.usedsize = bsize - flsize; + stats.freelistsize = flsize; + } +} + + + + +/* ============================ Gcx =============================== */ + +enum +{ PAGESIZE = 4096, + COMMITSIZE = (4096*16), + POOLSIZE = (4096*256), +} + +enum +{ + B_16, + B_32, + B_64, + B_128, + B_256, + B_512, + B_1024, + B_2048, + B_PAGE, // start of large alloc + B_PAGEPLUS, // continuation of large alloc + B_FREE, // free page + B_UNCOMMITTED, // memory not committed for this page + B_MAX +} + +alias ubyte Bins; + +struct List +{ + List *next; +} + +struct Range +{ + void *pbot; + void *ptop; +} + +const uint binsize[B_MAX] = [ 16,32,64,128,256,512,1024,2048,4096 ]; +const uint notbinsize[B_MAX] = [ ~(16u-1),~(32u-1),~(64u-1),~(128u-1),~(256u-1), + ~(512u-1),~(1024u-1),~(2048u-1),~(4096u-1) ]; + + +/* ============================ Gcx =============================== */ + +struct Gcx +{ + debug (THREADINVARIANT) + { + pthread_t self; + void thread_Invariant() + { + if (self != pthread_self()) + printf("thread_Invariant(): gcx = %x, self = %x, pthread_self() = %x\n", this, self, pthread_self()); + assert(self == pthread_self()); + } + } + else + { + void thread_Invariant() { } + } + + void *p_cache; + uint size_cache; + + uint nroots; + uint rootdim; + void **roots; + + uint nranges; + uint rangedim; + Range *ranges; + + uint noStack; // !=0 means don't scan stack + uint log; // turn on logging + uint anychanges; + void *stackBottom; + uint inited; + int disabled; // turn off collections if >0 + + byte *minAddr; // min(baseAddr) + byte *maxAddr; // max(topAddr) + + uint npools; + Pool **pooltable; + + List *bucket[B_MAX]; // free list for each size + + GC_FINALIZER finalizer; // finalizer function (one per GC) + + void init() + { int dummy; + + ((byte *)this)[0 .. Gcx.size] = 0; + stackBottom = (char *)&dummy; + log_init(); + debug (THREADINVARIANT) + self = pthread_self(); + //printf("gcx = %p, self = %x\n", this, self); + inited = 1; + } + + void Dtor() + { + inited = 0; + + for (uint i = 0; i < npools; i++) + { Pool *pool = pooltable[i]; + + pool.Dtor(); + c.stdlib.free(pool); + } + if (pooltable) + c.stdlib.free(pooltable); + + if (roots) + c.stdlib.free(roots); + + if (ranges) + c.stdlib.free(ranges); + } + + void Invariant() { } + + invariant + { + if (inited) + { + //printf("Gcx.invariant(): this = %p\n", this); + uint i; + + // Assure we're called on the right thread + debug (THREADINVARIANT) assert(self == pthread_self()); + + for (i = 0; i < npools; i++) + { Pool *pool = pooltable[i]; + + pool.Invariant(); + if (i == 0) + { + assert(minAddr == pool.baseAddr); + } + if (i + 1 < npools) + { + assert(pool.cmp(pooltable[i + 1]) < 0); + } + else if (i + 1 == npools) + { + assert(maxAddr == pool.topAddr); + } + } + + if (roots) + { + assert(rootdim != 0); + assert(nroots <= rootdim); + } + + if (ranges) + { + assert(rangedim != 0); + assert(nranges <= rangedim); + + for (i = 0; i < nranges; i++) + { + assert(ranges[i].pbot); + assert(ranges[i].ptop); + assert(ranges[i].pbot <= ranges[i].ptop); + } + } + + for (i = 0; i < B_PAGE; i++) + { + for (List *list = bucket[i]; list; list = list.next) + { + } + } + } + } + + + /*************************************** + */ + + void addRoot(void *p) + { + if (nroots == rootdim) + { + uint newdim = rootdim * 2 + 16; + void **newroots; + + newroots = (void **)c.stdlib.malloc(newdim * newroots[0].size); + assert(newroots); + if (roots) + { memcpy(newroots, roots, nroots * newroots[0].size); + c.stdlib.free(roots); + } + roots = newroots; + rootdim = newdim; + } + roots[nroots] = p; + nroots++; + } + + + void removeRoot(void *p) + { + uint i; + for (i = nroots; i--;) + { + if (roots[i] == p) + { + nroots--; + memmove(roots + i, roots + i + 1, (nroots - i) * roots[0].size); + return; + } + } + assert(0); + } + + /*************************************** + */ + + void addRange(void *pbot, void *ptop) + { + //debug(PRINTF) printf("Thread %x ", pthread_self()); + //debug(PRINTF) printf("%x.Gcx::addRange(%x, %x), nranges = %d\n", this, pbot, ptop, nranges); + if (nranges == rangedim) + { + uint newdim = rangedim * 2 + 16; + Range *newranges; + + newranges = (Range *)c.stdlib.malloc(newdim * newranges[0].size); + assert(newranges); + if (ranges) + { memcpy(newranges, ranges, nranges * newranges[0].size); + c.stdlib.free(ranges); + } + ranges = newranges; + rangedim = newdim; + } + ranges[nranges].pbot = pbot; + ranges[nranges].ptop = ptop; + nranges++; + } + + + void removeRange(void *pbot) + { + //debug(PRINTF) printf("Thread %x ", pthread_self()); + //debug(PRINTF) printf("%x.Gcx.removeRange(%x), nranges = %d\n", this, pbot, nranges); + for (uint i = nranges; i--;) + { + if (ranges[i].pbot == pbot) + { + nranges--; + memmove(ranges + i, ranges + i + 1, (nranges - i) * ranges[0].size); + return; + } + } + //debug(PRINTF) printf("Wrong thread\n"); + + // This is a fatal error, but ignore it. + // The problem is that we can get a Close() call on a thread + // other than the one the range was allocated on. + //assert(zero); + } + + /******************************* + * Find Pool that pointer is in. + * Return null if not in a Pool. + * Assume pooltable[] is sorted. + */ + + Pool *findPool(void *p) + { + if (p >= minAddr && p < maxAddr) + { + if (npools == 1) + { + return pooltable[0]; + } + + for (uint i = 0; i < npools; i++) + { Pool *pool; + + pool = pooltable[i]; + if (p < pool.topAddr) + { if (pool.baseAddr <= p) + return pool; + break; + } + } + } + return null; + } + + + /******************************* + * Find size of pointer p. + * Returns 0 if not a gc'd pointer + */ + + uint findSize(void *p) + { + Pool *pool; + uint size = 0; + + pool = findPool(p); + if (pool) + { + uint pagenum; + Bins bin; + + pagenum = ((uint)(p - pool.baseAddr)) / PAGESIZE; + bin = (Bins)pool.pagetable[pagenum]; + size = binsize[bin]; + if (bin == B_PAGE) + { uint npages = pool.ncommitted; + ubyte* pt; + uint i; + + pt = &pool.pagetable[0]; + for (i = pagenum + 1; i < npages; i++) + { + if (pt[i] != B_PAGEPLUS) + break; + } + size = (i - pagenum) * PAGESIZE; + } + } + return size; + } + + + /******************************* + * Compute bin for size. + */ + + static Bins findBin(uint size) + { Bins bin; + + if (size <= 256) + { + if (size <= 64) + { + if (size <= 16) + bin = B_16; + else if (size <= 32) + bin = B_32; + else + bin = B_64; + } + else + { + if (size <= 128) + bin = B_128; + else + bin = B_256; + } + } + else + { + if (size <= 1024) + { + if (size <= 512) + bin = B_512; + else + bin = B_1024; + } + else + { + if (size <= 2048) + bin = B_2048; + else + bin = B_PAGE; + } + } + return bin; + } + + /**************************************** + * Allocate a chunk of memory that is larger than a page. + * Return null if out of memory. + */ + + void *bigAlloc(uint size) + { + Pool *pool; + uint npages; + uint n; + uint pn; + uint freedpages; + void *p; + int state; + + npages = (size + PAGESIZE - 1) / PAGESIZE; + + for (state = 0; ; ) + { + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pn = pool.allocPages(npages); + if (pn != ~0u) + goto L1; + } + + // Failed + switch (state) + { + case 0: + // Try collecting + freedpages = fullcollectshell(); + if (freedpages >= npools * ((POOLSIZE / PAGESIZE) / 4)) + { state = 1; + continue; + } + // Allocate new pool + pool = newPool(npages); + if (!pool) + { state = 2; + continue; + } + pn = pool.allocPages(npages); + assert(pn != ~0u); + goto L1; + + case 1: + // Allocate new pool + pool = newPool(npages); + if (!pool) + goto Lnomemory; + pn = pool.allocPages(npages); + assert(pn != ~0u); + goto L1; + + case 2: + goto Lnomemory; + } + } + + L1: + pool.pagetable[pn] = B_PAGE; + if (npages > 1) + memset(&pool.pagetable[pn + 1], B_PAGEPLUS, npages - 1); + p = pool.baseAddr + pn * PAGESIZE; + memset((char *)p + size, 0, npages * PAGESIZE - size); + debug (MEMSTOMP) memset(p, 0xF1, size); + //debug(PRINTF) printf("\tp = %x\n", p); + return p; + + Lnomemory: + assert(0); + return null; + } + + + /*********************************** + * Allocate a new pool with at least npages in it. + * Sort it into pooltable[]. + * Return null if failed. + */ + + Pool *newPool(uint npages) + { + Pool *pool; + Pool **newpooltable; + uint newnpools; + uint i; + + //debug(PRINTF) printf("************Gcx::newPool(npages = %d)****************\n", npages); + + // Round up to COMMITSIZE pages + npages = (npages + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1); + + // Minimum of POOLSIZE + if (npages < POOLSIZE/PAGESIZE) + npages = POOLSIZE/PAGESIZE; + + // Allocate successively larger pools up to 8 megs + if (npools) + { uint n; + + n = npools; + if (n > 8) + n = 8; // cap pool size at 8 megs + n *= (POOLSIZE / PAGESIZE); + if (npages < n) + npages = n; + } + + pool = (Pool *)c.stdlib.calloc(1, Pool.size); + if (pool) + { + pool.init(npages); + if (!pool.baseAddr) + goto Lerr; + + newnpools = npools + 1; + newpooltable = (Pool **)c.stdlib.realloc(pooltable, newnpools * (Pool *).size); + if (!newpooltable) + goto Lerr; + + // Sort pool into newpooltable[] + for (i = 0; i < npools; i++) + { + if (pool.cmp(newpooltable[i]) < 0) + break; + } + memmove(newpooltable + i + 1, newpooltable + i, (npools - i) * (Pool *).size); + newpooltable[i] = pool; + + pooltable = newpooltable; + npools = newnpools; + + minAddr = pooltable[0].baseAddr; + maxAddr = pooltable[npools - 1].topAddr; + } + return pool; + + Lerr: + pool.Dtor(); + c.stdlib.free(pool); + return null; + } + + + /******************************* + * Allocate a page of bin's. + * Returns: + * 0 failed + */ + + int allocPage(Bins bin) + { + Pool *pool; + uint n; + uint pn; + byte *p; + byte *ptop; + + //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin); + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pn = pool.allocPages(1); + if (pn != ~0u) + goto L1; + } + return 0; // failed + + L1: + pool.pagetable[pn] = (ubyte)bin; + + // Convert page to free list + uint size = binsize[bin]; + List **b = &bucket[bin]; + + p = pool.baseAddr + pn * PAGESIZE; + ptop = p + PAGESIZE; + for (; p < ptop; p += size) + { + ((List *)p).next = *b; + *b = (List *)p; + } + return 1; + } + + + /************************************ + * Search a range of memory values and mark any pointers into the GC pool. + */ + + void mark(void *pbot, void *ptop) + { + void **p1 = (void **)pbot; + void **p2 = (void **)ptop; + uint changes = 0; + + //if (log) debug(PRINTF) printf("Gcx::mark(%x .. %x)\n", pbot, ptop); + for (; p1 < p2; p1++) + { + Pool *pool; + byte *p = (byte *)(*p1); + + //if (log) debug(PRINTF) printf("\tmark %x\n", p); + if (p >= minAddr) + { + pool = findPool(p); + if (pool) + { + uint offset = (uint)(p - pool.baseAddr); + uint biti; + uint pn = offset / PAGESIZE; + Bins bin = (Bins)pool.pagetable[pn]; + + //debug(PRINTF) printf("\t\tfound pool %x, base=%x, pn = %d, bin = %d, biti = x%x\n", pool, pool.baseAddr, pn, bin, biti); + + // Adjust bit to be at start of allocated memory block + if (bin <= B_PAGE) + { + biti = (offset & notbinsize[bin]) >> 4; + //debug(PRINTF) printf("\t\tbiti = x%x\n", biti); + } + else if (bin == B_PAGEPLUS) + { + do + { --pn; + } while ((Bins)pool.pagetable[pn] == B_PAGEPLUS); + biti = pn * (PAGESIZE / 16); + } + else + { + // Don't mark bits in B_FREE or B_UNCOMMITTED pages + continue; + } + + //debug(PRINTF) printf("\t\tmark(x%x) = %d\n", biti, pool.mark.test(biti)); + if (!pool.mark.test(biti)) + { + //if (log) debug(PRINTF) printf("\t\tmarking %x\n", p); + pool.mark.set(biti); + pool.scan.set(biti); + changes = 1; + log_parent(sentinel_add(pool.baseAddr + biti * 16), sentinel_add(pbot)); + } + } + } + } + anychanges |= changes; + } + + /********************************* + * Return number of full pages free'd. + */ + + uint fullcollectshell() + { + // The purpose of the 'shell' is to ensure all the registers + // get put on the stack so they'll be scanned + void *sp; + uint result; + asm + { + pushad ; + mov sp[EBP],ESP ; + } + result = fullcollect(sp); + asm + { + popad ; + } + return result; + } + + uint fullcollect(void *stackTop) + { + uint n; + Pool *pool; + + debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n"); + + Thread.pauseAll(); + + p_cache = null; + size_cache = 0; + + anychanges = 0; + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pool.mark.zero(); + pool.scan.zero(); + pool.freebits.zero(); + } + + // Mark each free entry, so it doesn't get scanned + for (n = 0; n < B_PAGE; n++) + { + for (List *list = bucket[n]; list; list = list.next) + { + pool = findPool(list); + assert(pool); + pool.freebits.set((uint)((byte *)list - pool.baseAddr) / 16); + } + } + + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pool.mark.copy(&pool.freebits); + } + + version (MULTI_THREADED) + { + // Scan stacks and registers for each paused thread + Thread[] threads = Thread.getAll(); + //thread_id id = cast(thread_id) GetCurrentThread(); + for (n = 0; n < threads.length; n++) + { Thread t = threads[n]; + + if (t && t.getState() == Thread.TS.RUNNING) + { + CONTEXT context; + + if (noStack && threads.length == 1) + break; + + context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; + if (!GetThreadContext(t.hdl, &context)) + { + assert(0); + } + debug (PRINTF) printf("mt scan stack bot = %x, top = %x\n", context.Esp, t.stackBottom); + mark((void *)context.Esp, t.stackBottom); + mark(&context.Edi, &context.Eip); + } + } + } + else + { + if (!noStack) + { + // Scan stack for main thread + debug(PRINTF) printf(" scan stack bot = %x, top = %x\n", stackTop, stackBottom); + version (STACKGROWSDOWN) + mark(stackTop, stackBottom); + else + mark(stackBottom, stackTop); + } + } + + // Scan roots[] + debug(COLLECT_PRINTF) printf("scan roots[]\n"); + mark(roots, roots + nroots); + + // Scan ranges[] + debug(COLLECT_PRINTF) printf("scan ranges[]\n"); + //log++; + for (n = 0; n < nranges; n++) + { + debug(COLLECT_PRINTF) printf("\t%x .. %x\n", ranges[n].pbot, ranges[n].ptop); + mark(ranges[n].pbot, ranges[n].ptop); + } + //log--; + + debug(COLLECT_PRINTF) printf("\tscan heap\n"); + while (anychanges) + { + anychanges = 0; + for (n = 0; n < npools; n++) + { + uint *bbase; + uint *b; + uint *btop; + + pool = pooltable[n]; + + bbase = pool.scan.base(); + btop = bbase + pool.scan.nwords; + for (b = bbase; b < btop;) + { Bins bin; + uint pn; + uint u; + uint bitm; + byte *o; + + bitm = *b; + if (!bitm) + { b++; + continue; + } + *b = 0; + + o = pool.baseAddr + (b - bbase) * 32 * 16; + if (!(bitm & 0xFFFF)) + { + bitm >>= 16; + o += 16 * 16; + } + for (; bitm; o += 16, bitm >>= 1) + { + if (!(bitm & 1)) + continue; + + pn = (o - pool.baseAddr) / PAGESIZE; + bin = (Bins)pool.pagetable[pn]; + if (bin < B_PAGE) + { + mark(o, o + binsize[bin]); + } + else if (bin == B_PAGE || bin == B_PAGEPLUS) + { + if (bin == B_PAGEPLUS) + { + while (pool.pagetable[pn - 1] != B_PAGE) + pn--; + } + u = 1; + while (pn + u < pool.ncommitted && pool.pagetable[pn + u] == B_PAGEPLUS) + u++; + mark(o, o + u * PAGESIZE); + } + } + } + } + } + + // Free up everything not marked + debug(COLLECT_PRINTF) printf("\tfree'ing\n"); + uint freedpages = 0; + uint freed = 0; + for (n = 0; n < npools; n++) + { uint pn; + uint ncommitted; + uint *bbase; + + pool = pooltable[n]; + bbase = pool.mark.base(); + ncommitted = pool.ncommitted; + for (pn = 0; pn < ncommitted; pn++, bbase += PAGESIZE / (32 * 16)) + { + Bins bin = (Bins)pool.pagetable[pn]; + + if (bin < B_PAGE) + { byte *p; + byte *ptop; + uint biti; + uint bitstride; + uint size = binsize[bin]; + + p = pool.baseAddr + pn * PAGESIZE; + ptop = p + PAGESIZE; + biti = pn * (PAGESIZE/16); + bitstride = size / 16; + + version(none) // BUG: doesn't work because freebits() must also be cleared + { + // If free'd entire page + if (bbase[0] == 0 && bbase[1] == 0 && bbase[2] == 0 && bbase[3] == 0 && + bbase[4] == 0 && bbase[5] == 0 && bbase[6] == 0 && bbase[7] == 0) + { + for (; p < ptop; p += size, biti += bitstride) + { + if (finalizer && pool.finals.nbits && + pool.finals.testClear(biti)) + { + (*finalizer)((List *)sentinel_add(p), null); + } + + List *list = (List *)p; + //debug(PRINTF) printf("\tcollecting %x\n", list); + log_free(sentinel_add(list)); + + debug (MEMSTOMP) memset(p, 0xF3, size); + } + pool.pagetable[pn] = B_FREE; + freed += PAGESIZE; + //debug(PRINTF) printf("freeing entire page %d\n", pn); + continue; + } + } + for (; p < ptop; p += size, biti += bitstride) + { + if (!pool.mark.test(biti)) + { + sentinel_Invariant(sentinel_add(p)); + + pool.freebits.set(biti); + if (finalizer && pool.finals.nbits && + pool.finals.testClear(biti)) + { + (*finalizer)((List *)sentinel_add(p), null); + } + + List *list = (List *)p; + debug(PRINTF) printf("\tcollecting %x\n", list); + log_free(sentinel_add(list)); + + debug (MEMSTOMP) memset(p, 0xF3, size); + + freed += size; + } + } + } + else if (bin == B_PAGE) + { uint biti = pn * (PAGESIZE / 16); + + if (!pool.mark.test(biti)) + { byte *p = pool.baseAddr + pn * PAGESIZE; + + sentinel_Invariant(sentinel_add(p)); + if (finalizer && pool.finals.nbits && + pool.finals.testClear(biti)) + { + (*finalizer)(sentinel_add(p), null); + } + + debug(COLLECT_PRINTF) printf("\tcollecting big %x\n", p); + log_free(sentinel_add(p)); + pool.pagetable[pn] = B_FREE; + freedpages++; + debug (MEMSTOMP) memset(p, 0xF3, PAGESIZE); + while (pn + 1 < ncommitted && pool.pagetable[pn + 1] == B_PAGEPLUS) + { + pn++; + pool.pagetable[pn] = B_FREE; + freedpages++; + + debug (MEMSTOMP) + { p += PAGESIZE; + memset(p, 0xF3, PAGESIZE); + } + } + } + } + } + } + + // Zero buckets + bucket[] = null; + + // Free complete pages, rebuild free list + debug(COLLECT_PRINTF) printf("\tfree complete pages\n"); + uint recoveredpages = 0; + for (n = 0; n < npools; n++) + { uint pn; + uint ncommitted; + + pool = pooltable[n]; + ncommitted = pool.ncommitted; + for (pn = 0; pn < ncommitted; pn++) + { + Bins bin = (Bins)pool.pagetable[pn]; + uint biti; + uint u; + + if (bin < B_PAGE) + { + uint size = binsize[bin]; + uint bitstride = size / 16; + uint bitbase = pn * (PAGESIZE / 16); + uint bittop = bitbase + (PAGESIZE / 16); + byte *p; + + biti = bitbase; + for (biti = bitbase; biti < bittop; biti += bitstride) + { if (!pool.freebits.test(biti)) + goto Lnotfree; + } + pool.pagetable[pn] = B_FREE; + recoveredpages++; + continue; + + Lnotfree: + p = pool.baseAddr + pn * PAGESIZE; + for (u = 0; u < PAGESIZE; u += size) + { biti = bitbase + u / 16; + if (pool.freebits.test(biti)) + { List *list; + + list = (List *)(p + u); + if (list.next != bucket[bin]) // avoid unnecessary writes + list.next = bucket[bin]; + bucket[bin] = list; + } + } + } + } + } + + debug(COLLECT_PRINTF) printf("recovered pages = %d\n", recoveredpages); + debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, npools); + + Thread.resumeAll(); + + return freedpages + recoveredpages; + } + + + /********************************* + * Run finalizer on p when it is free'd. + */ + + void doFinalize(void *p) + { + Pool *pool = findPool(p); + assert(pool); + + // Only allocate finals[] if we actually need it + if (!pool.finals.nbits) + pool.finals.alloc(pool.mark.nbits); + + pool.finals.set((p - pool.baseAddr) / 16); + } + + + + /***** Leak Detector ******/ + debug (LOGGING) + { + LogArray current; + LogArray prev; + + void log_init() + { + //debug(PRINTF) printf("+log_init()\n"); + current.reserve(1000); + prev.reserve(1000); + //debug(PRINTF) printf("-log_init()\n"); + } + + void log_malloc(void *p, uint size) + { + //debug(PRINTF) printf("+log_malloc(p = %x, size = %d)\n", p, size); + Log log; + + log.p = p; + log.size = size; + log.line = GC.line; + log.file = GC.file; + log.parent = null; + + GC.line = 0; + GC.file = null; + + current.push(log); + //debug(PRINTF) printf("-log_malloc()\n"); + } + + void log_free(void *p) + { + //debug(PRINTF) printf("+log_free(%x)\n", p); + uint i; + + i = current.find(p); + if (i == ~0u) + { + debug(PRINTF) printf("free'ing unallocated memory %x\n", p); + } + else + current.remove(i); + //debug(PRINTF) printf("-log_free()\n"); + } + + void log_collect() + { + //debug(PRINTF) printf("+log_collect()\n"); + // Print everything in current that is not in prev + + debug(PRINTF) printf("New pointers this cycle: --------------------------------\n"); + int used = 0; + for (uint i = 0; i < current.dim; i++) + { + uint j; + + j = prev.find(current.data[i].p); + if (j == ~0u) + current.data[i].print(); + else + used++; + } + + debug(PRINTF) printf("All roots this cycle: --------------------------------\n"); + for (uint i = 0; i < current.dim; i++) + { + void *p; + uint j; + + p = current.data[i].p; + if (!findPool(current.data[i].parent)) + { + j = prev.find(current.data[i].p); + if (j == ~0u) + debug(PRINTF) printf("N"); + else + debug(PRINTF) printf(" ");; + current.data[i].print(); + } + } + + debug(PRINTF) printf("Used = %d-------------------------------------------------\n", used); + prev.copy(¤t); + + debug(PRINTF) printf("-log_collect()\n"); + } + + void log_parent(void *p, void *parent) + { + //debug(PRINTF) printf("+log_parent()\n"); + uint i; + + i = current.find(p); + if (i == ~0u) + { + debug(PRINTF) printf("parent'ing unallocated memory %x, parent = %x\n", p, parent); + Pool *pool; + pool = findPool(p); + assert(pool); + uint offset = (uint)(p - pool.baseAddr); + uint biti; + uint pn = offset / PAGESIZE; + Bins bin = (Bins)pool.pagetable[pn]; + biti = (offset & notbinsize[bin]); + debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti); + } + else + { + current.data[i].parent = parent; + } + //debug(PRINTF) printf("-log_parent()\n"); + } + + } + else + { + void log_init() { } + void log_malloc(void *p, uint size) { } + void log_free(void *p) { } + void log_collect() { } + void log_parent(void *p, void *parent) { } + } +}; + +/* ============================ Pool =============================== */ + +struct Pool +{ + byte* baseAddr; + byte* topAddr; + GCBits mark; + GCBits scan; + GCBits finals; + GCBits freebits; + + uint npages; + uint ncommitted; // ncommitted <= npages + ubyte* pagetable; + + void init(uint npages) + { + uint poolsize; + + //debug(PRINTF) printf("Pool::Pool(%u)\n", npages); + poolsize = npages * PAGESIZE; + assert(poolsize >= POOLSIZE); + baseAddr = (byte *)os_mem_map(poolsize); + + // Some of the code depends on page alignment of memory pools + assert(((uint)baseAddr & (PAGESIZE - 1)) == 0); + + if (!baseAddr) + { + //debug(PRINTF) printf("GC fail: poolsize = x%x, errno = %d\n", poolsize, errno); + //debug(PRINTF) printf("message = '%s'\n", sys_errlist[errno]); + + npages = 0; + poolsize = 0; + } + //assert(baseAddr); + topAddr = baseAddr + poolsize; + + mark.alloc(poolsize / 16); + scan.alloc(poolsize / 16); + freebits.alloc(poolsize / 16); + + pagetable = (ubyte*)c.stdlib.malloc(npages); + memset(pagetable, B_UNCOMMITTED, npages); + + this.npages = npages; + ncommitted = 0; + } + + void Dtor() + { + if (baseAddr) + { + int result; + + if (ncommitted) + { + result = os_mem_decommit(baseAddr, 0, ncommitted * PAGESIZE); + assert(result == 0); + ncommitted = 0; + } + + if (npages) + { + result = os_mem_unmap(baseAddr, npages * PAGESIZE); + assert(result == 0); + npages = 0; + } + + baseAddr = null; + topAddr = null; + } + if (pagetable) + c.stdlib.free(pagetable); + + mark.Dtor(); + scan.Dtor(); + finals.Dtor(); + freebits.Dtor(); + } + + void Invariant() { } + + invariant + { + //mark.Invariant(); + //scan.Invariant(); + //finals.Invariant(); + //freebits.Invariant(); + + if (baseAddr) + { + //if (baseAddr + npages * PAGESIZE != topAddr) + //printf("baseAddr = %p, npages = %d, topAddr = %p\n", baseAddr, npages, topAddr); + assert(baseAddr + npages * PAGESIZE == topAddr); + assert(ncommitted <= npages); + } + + for (uint i = 0; i < npages; i++) + { Bins bin = (Bins)pagetable[i]; + + assert(bin < B_MAX); + } + } + + /************************************** + * Allocate n pages from Pool. + * Returns ~0u on failure. + */ + + uint allocPages(uint n) + { + uint i; + uint n2; + + //debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n); + n2 = n; + for (i = 0; i < ncommitted; i++) + { + if (pagetable[i] == B_FREE) + { + if (--n2 == 0) + { //debug(PRINTF) printf("\texisting pn = %d\n", i - n + 1); + return i - n + 1; + } + } + else + n2 = n; + } + if (ncommitted + n <= npages) + { + uint tocommit; + + tocommit = (n + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1); + if (ncommitted + tocommit > npages) + tocommit = npages - ncommitted; + //debug(PRINTF) printf("\tlooking to commit %d more pages\n", tocommit); + //fflush(stdout); + if (os_mem_commit(baseAddr, ncommitted * PAGESIZE, tocommit * PAGESIZE) == 0) + { + memset(pagetable + ncommitted, B_FREE, tocommit); + i = ncommitted; + ncommitted += tocommit; + + while (i && pagetable[i - 1] == B_FREE) + i--; + + return i; + } + //debug(PRINTF) printf("\tfailed to commit %d pages\n", tocommit); + } + + return ~0u; + } + + /********************************** + * Free npages pages starting with pagenum. + */ + + void freePages(uint pagenum, uint npages) + { + memset(&pagetable[pagenum], B_FREE, npages); + } + + /*************************** + * Used for sorting pooltable[] + */ + + int cmp(Pool *p2) + { + return baseAddr - p2.baseAddr; + } +} + + +/* ============================ SENTINEL =============================== */ + + +version (SENTINEL) +{ + const uint SENTINEL_PRE = 0xF4F4F4F4; // 32 bits + const ubyte SENTINEL_POST = 0xF5; // 8 bits + const uint SENTINEL_EXTRA = 2 * uint.size + 1; + + uint* sentinel_size(void *p) { return &((uint *)p)[-2]; } + uint* sentinel_pre(void *p) { return &((uint *)p)[-1]; } + ubyte* sentinel_post(void *p) { return &((ubyte *)p)[sentinel_size(p)]; } + + void sentinel_init(void *p, uint size) + { + *sentinel_size(p) = size; + *sentinel_pre(p) = SENTINEL_PRE; + *sentinel_post(p) = SENTINEL_POST; + } + + void sentinel_Invariant(void *p) + { + assert(*sentinel_pre(p) == SENTINEL_PRE); + assert(*sentinel_post(p) == SENTINEL_POST); + } + + void *sentinel_add(void *p) + { + return p + 2 * uint.size; + } + + void *sentinel_sub(void *p) + { + return p - 2 * uint.size; + } +} +else +{ + const uint SENTINEL_EXTRA = 0; + + void sentinel_init(void *p, uint size) + { + } + + void sentinel_Invariant(void *p) + { + } + + void *sentinel_add(void *p) + { + return p; + } + + void *sentinel_sub(void *p) + { + return p; + } +} + + diff --git a/gc2/makefile b/gc2/makefile new file mode 100644 index 000000000..3ab5eca64 --- /dev/null +++ b/gc2/makefile @@ -0,0 +1,46 @@ + +DMD=..\..\dmd +#DMD=\dmd\bin\dmd +CFLAGS=-g -mn -6 -r -Igc +DFLAGS=-unittest -g -release +CC=sc + +.c.obj: + $(CC) -c $(CFLAGS) $* + +.cpp.obj: + $(CC) -c $(CFLAGS) $* + +.d.obj: + $(DMD) -c $(DFLAGS) $* + +.asm.obj: + $(CC) -c $* + +targets : testgc.exe dmgc.lib + +testgc.exe : testgc.obj dmgc.lib + $(DMD) testgc.obj dmgc.lib -g + +testgc.obj : testgc.d + +OBJS= gc.obj gcx.obj gcbits.obj win32.obj + +SRC= gc.d gcx.d gcbits.d win32.d testgc.d + +dmgc.lib : $(OBJS) makefile + del dmgc.lib + lib dmgc /c/noi +gc+gcx+gcbits+win32; + +gc.obj : gc.d +gcx.obj : gcx.d +gcbits.obj : gcbits.d +win32.obj : win32.d + +zip : makefile $(SRC) + zip32 -u dmgc makefile $(SRC) + +clean: + del $(OBJS) + del dmgc.lib + del testgc.exe diff --git a/gc/testgc.c b/gc2/testgc.d similarity index 54% rename from gc/testgc.c rename to gc2/testgc.d index b2b617c51..31835bb6f 100644 --- a/gc/testgc.c +++ b/gc2/testgc.d @@ -1,51 +1,55 @@ -// Copyright (C) 2001 by Digital Mars +// Copyright (C) 2001-2002 by Digital Mars // All Rights Reserved // Written by Walter Bright +// www.digitalmars.com // GC tester program -#include -#include -#include -#include +import c.stdio; +import c.stdlib; +import string; -#include "gc.h" - -void *stackbottom; +import gcstats; +import gc; +import gcx; +import random; void printStats(GC *gc) { GCStats stats; - gc->getStats(&stats); + gc.getStats(stats); printf("poolsize = x%x, usedsize = x%x, freelistsize = x%x, freeblocks = %d, pageblocks = %d\n", stats.poolsize, stats.usedsize, stats.freelistsize, stats.freeblocks, stats.pageblocks); } -#define PERMUTE(key) (key + 1) - -void fill(void *p, unsigned key, unsigned size) +uint PERMUTE(uint key) { - unsigned i; - char *q = (char *)p; + return key + 1; +} + +void fill(void *p, uint key, uint size) +{ + uint i; + byte *q = (byte *)p; for (i = 0; i < size; i++) { key = PERMUTE(key); - q[i] = (char)key; + q[i] = (byte)key; } } -void verify(void *p, unsigned key, unsigned size) +void verify(void *p, uint key, uint size) { - unsigned i; - char *q = (char *)p; + uint i; + byte *q = (byte *)p; for (i = 0; i < size; i++) { key = PERMUTE(key); - assert(q[i] == (char)key); + assert(q[i] == (byte)key); } } @@ -62,39 +66,36 @@ void smoke() printf("--------------------------smoke()\n"); - gc = new GC(); - delete gc; + gc = newGC(); + deleteGC(gc); - gc = new GC(); - gc->init(); - delete gc; + gc = newGC(); + gc.init(); + deleteGC(gc); - gc = new GC(); - gc->init(); - //gc->setStackBottom(stackbottom); - char *p = (char *)gc->malloc(10); + gc = newGC(); + gc.init(); + char *p = (char *)gc.malloc(10); assert(p); strcpy(p, "Hello!"); - char *p2 = gc->strdup(p); - printf("p2 = %x, '%s'\n", p2, p2); - int result = strcmp(p, p2); - assert(result == 0); - gc->strdup(p); +// char *p2 = gc.strdup(p); +// printf("p2 = %x, '%s'\n", p2, p2); +// int result = strcmp(p, p2); +// assert(result == 0); +// gc.strdup(p); printf("p = %x\n", p); - p = NULL; - gc->fullcollect(); + p = null; + gc.fullCollect(); printStats(gc); - delete gc; + deleteGC(gc); } /* ---------------------------- */ void finalizer(void *p, void *dummy) { - (void)p; - (void)dummy; } void smoke2() @@ -103,21 +104,20 @@ void smoke2() int *p; int i; - #define SMOKE2_SIZE 100 + const int SMOKE2_SIZE = 100; int *foo[SMOKE2_SIZE]; printf("--------------------------smoke2()\n"); - gc = new GC(); - gc->init(); - //gc->setStackBottom(stackbottom); + gc = newGC(); + gc.init(); for (i = 0; i < SMOKE2_SIZE; i++) { - p = (int *)gc->calloc(i + 1, 500); + p = (int *)gc.calloc(i + 1, 500); p[0] = i * 3; foo[i] = p; - gc->setFinalizer(p, finalizer); + gc.setFinalizer((void *)p, &finalizer); } for (i = 0; i < SMOKE2_SIZE; i += 2) @@ -126,19 +126,19 @@ void smoke2() if (p[0] != i * 3) { printf("p = %x, i = %d, p[0] = %d\n", p, i, p[0]); - fflush(stdout); + //c.stdio.fflush(stdout); } assert(p[0] == i * 3); - gc->free(p); + gc.free(p); } - p = NULL; - memset(foo, 0, sizeof(foo)); + p = null; + foo[] = null; - gc->fullcollect(); + gc.fullCollect(); printStats(gc); - delete gc; + deleteGC(gc); } /* ---------------------------- */ @@ -151,28 +151,27 @@ void smoke3() printf("--------------------------smoke3()\n"); - gc = new GC(); - gc->init(); - //gc->setStackBottom(stackbottom); + gc = newGC(); + gc.init(); - for (i = 0; i < 1000000; i++) -// for (i = 0; i < 1000; i++) +// for (i = 0; i < 1000000; i++) + for (i = 0; i < 1000; i++) { - unsigned size = rand() % 2048; - p = (int *)gc->malloc(size); + uint size = rand() % 2048; + p = (int *)gc.malloc(size); memset(p, i, size); size = rand() % 2048; - p = (int *)gc->realloc(p, size); + p = (int *)gc.realloc(p, size); memset(p, i + 1, size); } - p = NULL; + p = null; desregs(); - gc->fullcollect(); + gc.fullCollect(); printStats(gc); - delete gc; + deleteGC(gc); } /* ---------------------------- */ @@ -185,58 +184,56 @@ void smoke4() printf("--------------------------smoke4()\n"); - gc = new GC(); - gc->init(); - //gc->setStackBottom(stackbottom); + gc = newGC(); + gc.init(); for (i = 0; i < 80000; i++) { - unsigned size = i; - p = (int *)gc->malloc(size); + uint size = i; + p = (int *)gc.malloc(size); memset(p, i, size); size = rand() % 2048; - gc->check(p); - p = (int *)gc->realloc(p, size); + gc.check(p); + p = (int *)gc.realloc(p, size); memset(p, i + 1, size); } - p = NULL; + p = null; desregs(); - gc->fullcollect(); + gc.fullCollect(); printStats(gc); - delete gc; + deleteGC(gc); } /* ---------------------------- */ void smoke5(GC *gc) { - char *p; + byte *p; int i; int j; - #define SMOKE5_SIZE 1000 - char *array[SMOKE5_SIZE]; - unsigned offset[SMOKE5_SIZE]; + const int SMOKE5_SIZE = 1000; + byte *array[SMOKE5_SIZE]; + uint offset[SMOKE5_SIZE]; printf("--------------------------smoke5()\n"); - - memset(array, 0, sizeof(array)); - memset(offset, 0, sizeof(offset)); + //printf("gc = %p\n", gc); + //printf("gc = %p, gcx = %p, self = %x\n", gc, gc.gcx, gc.gcx.self); for (j = 0; j < 20; j++) { - for (i = 0; i < 4000; i++) + for (i = 0; i < 2000 /*4000*/; i++) { - unsigned size = (rand() % 2048) + 1; - unsigned index = rand() % SMOKE5_SIZE; + uint size = (rand() % 2048) + 1; + uint index = rand() % SMOKE5_SIZE; //printf("index = %d, size = %d\n", index, size); p = array[index] - offset[index]; - p = (char *)gc->realloc(p, size); + p = (byte *)gc.realloc(p, size); if (array[index]) - { unsigned s; + { uint s; //printf("\tverify = %d\n", p[0]); s = offset[index]; @@ -251,12 +248,12 @@ void smoke5(GC *gc) //printf("p[0] = %d\n", p[0]); } - gc->fullcollect(); + gc.fullCollect(); } - p = NULL; - memset(array, 0, sizeof(array)); - gc->fullcollect(); + p = null; + array[] = null; + gc.fullCollect(); printStats(gc); } @@ -270,21 +267,30 @@ int main(int argc, char *argv[]) printf("GC test start\n"); - (void)argc; - stackbottom = &argv; - - gc = new GC(); - gc->init(); - //gc->setStackBottom(stackbottom); + gc = newGC(); +printf("gc = %p\n", gc); + gc.init(); smoke(); smoke2(); smoke3(); smoke4(); +printf("gc = %p\n", gc); smoke5(gc); - delete gc; + deleteGC(gc); printf("GC test success\n"); return EXIT_SUCCESS; } + +GC *newGC() +{ + return (GC *)c.stdlib.calloc(1, GC.size); +} + +void deleteGC(GC *gc) +{ + gc.Dtor(); + c.stdlib.free(gc); +} diff --git a/gc/win32.c b/gc2/win32.d similarity index 64% rename from gc/win32.c rename to gc2/win32.d index 073906d0e..2049340de 100644 --- a/gc/win32.c +++ b/gc2/win32.d @@ -1,15 +1,20 @@ -#include +// Copyright (C) 2001-2002 by Digital Mars +// All Rights Reserved +// www.digitalmars.com +// Written by Walter Bright -#include "os.h" +import windows; + +alias int pthread_t; /*********************************** * Map memory. */ -void *os_mem_map(unsigned nbytes) +void *os_mem_map(uint nbytes) { - return VirtualAlloc(NULL, nbytes, MEM_RESERVE, PAGE_READWRITE); + return VirtualAlloc(null, nbytes, MEM_RESERVE, PAGE_READWRITE); } /*********************************** @@ -19,12 +24,12 @@ void *os_mem_map(unsigned nbytes) * !=0 failure */ -int os_mem_commit(void *base, unsigned offset, unsigned nbytes) +int os_mem_commit(void *base, uint offset, uint nbytes) { void *p; - p = VirtualAlloc((char *)base + offset, nbytes, MEM_COMMIT, PAGE_READWRITE); - return (p == NULL); + p = VirtualAlloc(base + offset, nbytes, MEM_COMMIT, PAGE_READWRITE); + return (p == null); } @@ -35,9 +40,9 @@ int os_mem_commit(void *base, unsigned offset, unsigned nbytes) * !=0 failure */ -int os_mem_decommit(void *base, unsigned offset, unsigned nbytes) +int os_mem_decommit(void *base, uint offset, uint nbytes) { - return VirtualFree((char *)base + offset, nbytes, MEM_DECOMMIT) == 0; + return VirtualFree(base + offset, nbytes, MEM_DECOMMIT) == 0; } /*********************************** @@ -48,9 +53,8 @@ int os_mem_decommit(void *base, unsigned offset, unsigned nbytes) * !=0 failure */ -int os_mem_unmap(void *base, unsigned nbytes) +int os_mem_unmap(void *base, uint nbytes) { - (void)nbytes; return VirtualFree(base, 0, MEM_RELEASE) == 0; } @@ -60,6 +64,7 @@ int os_mem_unmap(void *base, unsigned nbytes) pthread_t pthread_self() { + //printf("pthread_self() = %x\n", GetCurrentThreadId()); return (pthread_t) GetCurrentThreadId(); } @@ -67,21 +72,13 @@ pthread_t pthread_self() * Determine "bottom" of stack (actually the top on Win32 systems). */ -#if __DMC__ -extern "C" -{ - extern void * __cdecl _atopsp; -} -#endif - void *os_query_stackBottom() { -#if __DMC__ - //return _atopsp; // not needed -#endif - _asm + asm { - mov EAX, FS:4 + naked ; + mov EAX,FS:4 ; + ret ; } } @@ -89,30 +86,29 @@ void *os_query_stackBottom() * Determine base address and size of static data segment. */ -#if __DMC__ -extern "C" +extern (C) { - extern int __cdecl _xi_a; // &_xi_a just happens to be start of data segment - extern int __cdecl _edata; // &_edata is start of BSS segment - extern int __cdecl _end; // &_end is past end of BSS + extern int _xi_a; // &_xi_a just happens to be start of data segment + extern int _edata; // &_edata is start of BSS segment + extern int _end; // &_end is past end of BSS } -void os_query_staticdataseg(void **base, unsigned *nbytes) +void os_query_staticdataseg(void **base, uint *nbytes) { *base = (void *)&_xi_a; - *nbytes = (unsigned)((char *)&_end - (char *)&_xi_a); + *nbytes = (uint)((char *)&_end - (char *)&_xi_a); } -#endif -#if _MSC_VER -void os_query_staticdataseg(void **base, unsigned *nbytes) +/++++ + +void os_query_staticdataseg(void **base, uint *nbytes) { static char dummy = 6; SYSTEM_INFO si; MEMORY_BASIC_INFORMATION mbi; char *p; - void *bottom = NULL; - unsigned size = 0; + void *bottom = null; + uint size = 0; // Tests show the following does not work reliably. // The reason is that the data segment is arbitrarilly divided @@ -122,14 +118,14 @@ void os_query_staticdataseg(void **base, unsigned *nbytes) assert(0); // fix implementation GetSystemInfo(&si); - p = (char *)((unsigned)(&dummy) & ~(si.dwPageSize - 1)); + p = (char *)((uint)(&dummy) & ~(si.dwPageSize - 1)); while (VirtualQuery(p, &mbi, sizeof(mbi)) == sizeof(mbi) && mbi.Protect & (PAGE_READWRITE | PAGE_WRITECOPY) && !(mbi.Protect & PAGE_GUARD) && mbi.AllocationBase != 0) { bottom = (void *)mbi.BaseAddress; - size = (unsigned)mbi.RegionSize; + size = (uint)mbi.RegionSize; printf("dwPageSize = x%x\n", si.dwPageSize); printf("&dummy = %p\n", &dummy); @@ -147,4 +143,5 @@ void os_query_staticdataseg(void **base, unsigned *nbytes) *base = bottom; *nbytes = size; } -#endif + +++++/ diff --git a/gcstats.d b/gcstats.d new file mode 100644 index 000000000..6039c0a87 --- /dev/null +++ b/gcstats.d @@ -0,0 +1,15 @@ +// Copyright (c) 1999-2002 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com + +struct GCStats +{ + uint poolsize; // total size of pool + uint usedsize; // bytes allocated + uint freeblocks; // number of blocks marked FREE + uint freelistsize; // total of memory on free lists + uint pageblocks; // number of blocks marked PAGE +} + + diff --git a/interface.c b/interface.c deleted file mode 100644 index 223e056b5..000000000 --- a/interface.c +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2000 by Digital Mars -// All Rights Reserved -// Written by Walter Bright - -#include -#include -#include -#include - -#include "mars.h" - -/********************************************* - * Return pointer to interface function. - */ - -void * __stdcall _d_interface(Object *o, unsigned vindex, ClassInfo *interface) -{ ClassInfo *cb; - int i; - - //printf("_d_interface(o = %p, vindex = x%x, interface = %p)\n",o,vindex,interface); - cb = (ClassInfo *)o->vptr[0]; - if (cb == interface) - return o->vptr[vindex]; - for (i = 0; i < cb->interfacelen; i++) - { int x; - - if (cb->interfaces[i].classinfo == interface) - { - return cb->interfaces[i].vtbl.vptr[vindex]; - } - } - return NULL; -} diff --git a/intrinsic.d b/intrinsic.d new file mode 100644 index 000000000..1998ef100 --- /dev/null +++ b/intrinsic.d @@ -0,0 +1,31 @@ + + +// Copyright (c) 1999-2002 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com + +/* These functions are built-in intrinsics to the compiler. + */ + +int bsf(uint v); +int bsr(uint v); +int bt(uint *p, uint bitnum); +int btc(uint *p, uint bitnum); +int btr(uint *p, uint bitnum); +int bts(uint *p, uint bitnum); + +ubyte inp(uint); +ushort inpw(uint); +uint inpl(uint); + +ubyte outp(uint, ubyte); +ushort outpw(uint, ushort); +uint outpl(uint, uint); + +extended cos(extended); +extended fabs(extended); +extended rint(extended); +long rndtol(extended); +extended sin(extended); +extended sqrt(extended); diff --git a/invariant.d b/invariant.d index c78ef8cf4..3ea9db211 100644 --- a/invariant.d +++ b/invariant.d @@ -7,7 +7,7 @@ void _d_invariant(Object o) //printf("__d_invariant(%p)\n", o); // BUG: needs to be filename/line of caller, not library routine - assert(o != null); // just do null check, not invariant check + assert(o !== null); // just do null check, not invariant check c = o.classinfo; do diff --git a/iunknown.d b/iunknown.d index 5f3e9571a..5ff2238d9 100644 --- a/iunknown.d +++ b/iunknown.d @@ -6,7 +6,7 @@ alias int HRESULT; enum : int { S_OK = 0, - E_NOINTERFACE = 0x80004002, + E_NOINTERFACE = cast(int)0x80004002, } struct GUID { // size is 16 diff --git a/makefile b/makefile index 350298e12..fe0bdc232 100644 --- a/makefile +++ b/makefile @@ -1,7 +1,22 @@ +# Makefile to build D runtime library phobos.lib. +# Designed to work with \dm\bin\make.exe +# Targets: +# make +# Same as make unittest +# make phobos.lib +# Build phobos.lib +# make clean +# Delete unneeded files created by build process +# make unittest +# Build phobos.lib, build and run unit tests +# Notes: +# This relies on LIB.EXE 8.00 or later, and MAKE.EXE 5.01 or later. -CFLAGS=-g -mn -6 -r -Igc +CFLAGS=-g -mn -6 -r DFLAGS=-unittest -g CC=sc +#DMD=\dmd\bin\dmd +DMD=..\dmd .c.obj: $(CC) -c $(CFLAGS) $* @@ -10,7 +25,7 @@ CC=sc $(CC) -c $(CFLAGS) $* .d.obj: - ..\dmd $(DFLAGS) $* + $(DMD) -c $(DFLAGS) $* .asm.obj: $(CC) -c $* @@ -23,27 +38,33 @@ unittest : unittest.exe test : test.exe test.obj : test.d - ..\dmd test -g + $(DMD) -c test -g -test.exe : test.obj phobos.lib ..\dmd.exe - sc test.obj -g +test.exe : test.obj phobos.lib + $(DMD) test.obj -g -unittest.exe : unittest.d phobos.lib ..\dmd.exe - ..\dmd unittest -g +unittest.exe : unittest.d phobos.lib + $(DMD) unittest -g sc unittest.obj -g -OBJS= assert.obj deh.obj modulo.obj new.obj switch.obj complex.obj \ +OBJS= assert.obj deh.obj switch.obj complex.obj gcstats.obj \ critical.obj object.obj monitor.obj arraycat.obj invariant.obj \ - dmain2.obj outofmemory.obj achar.obj aaAh4.obj adi.obj file.obj \ - compiler.obj system.obj arraysetlength.obj minit.obj moduleinit.obj \ + dmain2.obj outofmemory.obj achar.obj aaA.obj adi.obj file.obj \ + compiler.obj system.obj moduleinit.obj \ cast.obj syserror.obj path.obj string.obj memset.obj math.obj \ outbuffer.obj ctype.obj regexp.obj random.obj windows.obj \ - stream.obj switcherr.obj com.obj array.obj + stream.obj switcherr.obj com.obj array.obj gc.obj adi.obj \ + qsort.obj math2.obj date.obj dateparse.obj thread.obj obj.obj \ + iunknown.obj crc32.obj conv.obj arraycast.obj \ + ti_Aa.obj ti_C.obj ti_int.obj ti_char.obj \ + ti_wchar.obj ti_uint.obj ti_short.obj ti_ushort.obj \ + ti_byte.obj ti_ubyte.obj ti_long.obj ti_ulong.obj ti_ptr.obj \ + ti_float.obj ti_double.obj ti_extended.obj ti_delegate.obj HDR=mars.h -SRC= modulo.c new.cpp switch.d complex.c critical.c fpu.d \ - aa.c vaa.c interface.c arraysetlength.cpp minit.asm +SRC= switch.d complex.c critical.c fpu.d \ + aa.c vaa.c interface.c minit.asm SRC2=deh.c object.d gc.d math.d c\stdio.d c\stdlib.d time.d monitor.c arraycat.d \ string.d windows.d path.d @@ -51,23 +72,25 @@ SRC2=deh.c object.d gc.d math.d c\stdio.d c\stdlib.d time.d monitor.c arraycat.d SRC3=invariant.d assert.d RegExp.d dmain2.d dateparse.d \ outofmemory.d syserror.d -SRC4=dchar.d ctype.d achar.d aaAh4.d adi.d file.d compiler.d system.d \ - moduleinit.d cast.d math.d +SRC4=dchar.d ctype.d achar.d aaA.d adi.d file.d compiler.d system.d \ + moduleinit.d cast.d math.d qsort.d -SRC5=outbuffer.d unittest.d stream.d ctype.d regexp.d random.d +SRC5=outbuffer.d unittest.d stream.d ctype.d regexp.d random.d adi.d \ + ti_Aa.d ti_C.d ti_int.d ti_char.d -phobos.lib : $(OBJS) gc\dmgc.lib makefile - del phobos.lib - lib phobos /c/noi +critical+assert+deh+object+arraysetlength; - lib phobos /noi +modulo+new+switch+monitor+string; - lib phobos /noi +arraycat+invariant+dmain2+achar+outofmemory; - lib phobos /noi +aaAh4+adi+file+compiler+system+syserror+stream; - lib phobos /noi +math+outbuffer+ctype+regexp+random+windows+switcherr; - lib phobos /noi +com+array; - lib phobos /noi +minit+moduleinit+cast+path+memset+gc\dmgc.lib; +SRC6=math2.d thread.d obj.d iunknown.d intrinsic.d time.d memset.c \ + array.d switcherr.d arraycast.d +SRC7=ti_wchar.d ti_uint.d ti_short.d ti_ushort.d \ + ti_byte.d ti_ubyte.d ti_long.d ti_ulong.d ti_ptr.d \ + ti_float.d ti_double.d ti_extended.d ti_delegate.d -aaAh4.obj : aaAh4.d +SRC8=crc32.d stdint.d conv.d gcstats.d + +phobos.lib : $(OBJS) minit.obj gc2\dmgc.lib makefile + lib -c phobos.lib $(OBJS) minit.obj gc2\dmgc.lib + +aaA.obj : aaA.d achar.obj : achar.d adi.obj : adi.d arraycat.obj : arraycat.d @@ -77,27 +100,39 @@ compiler.obj : compiler.d complex.obj : mars.h complex.c critical.obj : mars.h critical.c dassert.obj : mars.h dassert.c +date.obj : dateparse.d date.d +dateparse.obj : dateparse.d date.d +deh.obj : mars.h deh.c dmain2.obj : dmain2.d file.obj : file.d +gc.obj : gc.d invariant.obj : invariant.d +math.obj : math.d +math2.obj : math2.d minit.obj : minit.asm moduleinit.obj : moduleinit.d -modulo.obj : mars.h modulo.c -monitor.obj : mars.h mars.h monitor.c +monitor.obj : mars.h monitor.c outofmemory.obj : outofmemory.d +qsort.obj : qsort.d switch.obj : switch.d system.obj : system.d +thread.obj : thread.d +ti_Aa.obj : ti_Aa.d +ti_C.obj : ti_C.d +ti_char.obj : ti_char.d +ti_int.obj : ti_int.d -arraysetlength.obj : mars.h arraysetlength.cpp -new.obj : mars.h new.cpp - -zip : makefile $(HDR) $(SRC) $(SRC2) $(SRC3) $(SRC4) $(SRC5) +zip : makefile $(HDR) $(SRC) $(SRC2) $(SRC3) $(SRC4) $(SRC5) $(SRC6) $(SRC7) \ + $(SRC8) zip32 -u phobos makefile $(HDR) zip32 -u phobos $(SRC) zip32 -u phobos $(SRC2) zip32 -u phobos $(SRC3) zip32 -u phobos $(SRC4) zip32 -u phobos $(SRC5) + zip32 -u phobos $(SRC6) + zip32 -u phobos $(SRC7) + zip32 -u phobos $(SRC8) clean: - del *.obj + del $(OBJS) diff --git a/mars.h b/mars.h index 7a8bc6045..fcd1c7406 100644 --- a/mars.h +++ b/mars.h @@ -63,8 +63,8 @@ void _d_monitorrelease(Object *h); int _d_isbaseof(ClassInfo *b, ClassInfo *c); Object *_d_dynamic_cast(Object *o, ClassInfo *ci); -Object * __ddecl __d_newclass(ClassInfo *ci); -void __ddecl _d_delclass(Object **p); +Object * _d_newclass(ClassInfo *ci); +void _d_delclass(Object **p); void _d_OutOfMemory(); diff --git a/math2.d b/math2.d new file mode 100644 index 000000000..721e3f264 --- /dev/null +++ b/math2.d @@ -0,0 +1,1359 @@ +/* + * Copyright (c) 2002 + * Pavel "EvilOne" Minayev + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Author makes no representations about + * the suitability of this software for any purpose. It is provided + * "as is" without express or implied warranty. + */ + +module math2; +import intrinsic; +private import math, string, c.stdlib, c.stdio; + +//debug=math2; + +/**************************************** + * compare floats with given precision + */ + +bit feq(extended a, extended b) +{ + return feq(a, b, 0.000001); +} + +bit feq(extended a, extended b, extended eps) +{ + return abs(a - b) <= eps; +} + +/********************************* + * Modulus + */ + +int abs(int n) +{ + return n > 0 ? n : -n; +} + +long abs(long n) +{ + return n > 0 ? n : -n; +} + +extended abs(extended n) +{ + // just let the compiler handle it + return intrinsic.fabs(n); +} + +/********************************* + * Square + */ + +int sqr(int n) +{ + return n * n; +} + +long sqr(long n) +{ + return n * n; +} + +extended sqr(extended n) +{ + return n * n; +} + +unittest +{ + assert(sqr(sqr(3)) == 81); +} + +private ushort fp_cw_chop = 7999; + +/********************************* + * Integer part + */ + +extended trunc(extended n) +{ + ushort cw; + asm + { + fstcw cw; + fldcw fp_cw_chop; + fld n; + frndint; + fldcw cw; + } +} + +unittest +{ + assert(feq(trunc(+123.456), +123.0L)); + assert(feq(trunc(-123.456), -123.0L)); +} + +/********************************* + * Fractional part + */ + +extended frac(extended n) +{ + return n - trunc(n); +} + +unittest +{ + assert(feq(frac(+123.456), +0.456L)); + assert(feq(frac(-123.456), -0.456L)); +} + +/********************************* + * Polynomial of X + */ + +extended poly(extended x, extended[] coefficients) +{ + debug (math2) printf("poly(), coefficients.length = %d\n", coefficients.length); + if (!coefficients.length) + return 0; + extended result = coefficients[coefficients.length - 1]; + for (int i = coefficients.length - 2; i >= 0; i--) + result = result * x + coefficients[i]; + return result; +} + +unittest +{ + debug (math2) printf("unittest.poly()\n"); + static extended[4] k = [ 4, 3, 2, 1 ]; + assert(feq(poly(2, k), cast(extended) 8 * 1 + 4 * 2 + 2 * 3 + 4)); +} + +/********************************* + * Sign + */ + +int sign(int n) +{ + return (n > 0 ? +1 : (n < 0 ? -1 : 0)); +} + +unittest +{ + assert(sign(0) == 0); + assert(sign(+666) == +1); + assert(sign(-666) == -1); +} + +int sign(long n) +{ + return (n > 0 ? +1 : (n < 0 ? -1 : 0)); +} + +unittest +{ + assert(sign(0) == 0); + assert(sign(+666L) == +1); + assert(sign(-666L) == -1); +} + +int sign(extended n) +{ + return (n > 0 ? +1 : (n < 0 ? -1 : 0)); +} + +unittest +{ + assert(sign(0.0L) == 0); + assert(sign(+123.456L) == +1); + assert(sign(-123.456L) == -1); +} + +/********************************************************** + * Cycles <-> radians <-> grads <-> degrees conversions + */ + +extended cycle2deg(extended c) +{ + return c * 360; +} + +extended cycle2rad(extended c) +{ + return c * PI * 2; +} + +extended cycle2grad(extended c) +{ + return c * 400; +} + +extended deg2cycle(extended d) +{ + return d / 360; +} + +extended deg2rad(extended d) +{ + return d / 180 * PI; +} + +extended deg2grad(extended d) +{ + return d / 90 * 100; +} + +extended rad2deg(extended r) +{ + return r / PI * 180; +} + +extended rad2cycle(extended r) +{ + return r / (PI * 2); +} + +extended rad2grad(extended r) +{ + return r / PI * 200; +} + +extended grad2deg(extended g) +{ + return g / 100 * 90; +} + +extended grad2cycle(extended g) +{ + return g / 400; +} + +extended grad2rad(extended g) +{ + return g / 200 * PI; +} + +unittest +{ + assert(feq(cycle2deg(0.5), 180)); + assert(feq(cycle2rad(0.5), PI)); + assert(feq(cycle2grad(0.5), 200)); + assert(feq(deg2cycle(180), 0.5)); + assert(feq(deg2rad(180), PI)); + assert(feq(deg2grad(180), 200)); + assert(feq(rad2deg(PI), 180)); + assert(feq(rad2cycle(PI), 0.5)); + assert(feq(rad2grad(PI), 200)); + assert(feq(grad2deg(200), 180)); + assert(feq(grad2cycle(200), 0.5)); + assert(feq(grad2rad(200), PI)); +} + +/************************************ + * Arithmetic average of values + */ + +extended avg(extended[] n) +{ + extended result = 0; + for (uint i = 0; i < n.length; i++) + result += n[i]; + return result / n.length; +} + +unittest +{ + static extended[4] n = [ 1, 2, 4, 5 ]; + assert(feq(avg(n), 3)); +} + +/************************************* + * Sum of values + */ + +int sum(int[] n) +{ + long result = 0; + for (uint i = 0; i < n.length; i++) + result += n[i]; + return result; +} + +unittest +{ + static int[3] n = [ 1, 2, 3 ]; + assert(sum(n) == 6); +} + +long sum(long[] n) +{ + long result = 0; + for (uint i = 0; i < n.length; i++) + result += n[i]; + return result; +} + +unittest +{ + static long[3] n = [ 1, 2, 3 ]; + assert(sum(n) == 6); +} + +extended sum(extended[] n) +{ + extended result = 0; + for (uint i = 0; i < n.length; i++) + result += n[i]; + return result; +} + +unittest +{ + static extended[3] n = [ 1, 2, 3 ]; + assert(feq(sum(n), 6)); +} + +/************************************* + * The smallest value + */ + +int min(int[] n) +{ + int result = int.max; + for (uint i = 0; i < n.length; i++) + if (n[i] < result) + result = n[i]; + return result; +} + +unittest +{ + static int[3] n = [ 2, -1, 0 ]; + assert(min(n) == -1); +} + +long min(long[] n) +{ + long result = long.max; + for (uint i = 0; i < n.length; i++) + if (n[i] < result) + result = n[i]; + return result; +} + +unittest +{ + static long[3] n = [ 2, -1, 0 ]; + assert(min(n) == -1); +} + +extended min(extended[] n) +{ + extended result = extended.max; + for (uint i = 0; i < n.length; i++) + { + if (n[i] < result) + result = n[i]; + } + return result; +} + +unittest +{ + static extended[3] n = [ 2.0, -1.0, 0.0 ]; + assert(feq(min(n), -1)); +} + +int min(int a, int b) +{ + return a < b ? a : b; +} + +unittest +{ + assert(min(1, 2) == 1); +} + +long min(long a, long b) +{ + return a < b ? a : b; +} + +unittest +{ + assert(min(1L, 2L) == 1); +} + +extended min(extended a, extended b) +{ + return a < b ? a : b; +} + +unittest +{ + assert(feq(min(1.0L, 2.0L), 1.0L)); +} + +/************************************* + * The largest value + */ + +int max(int[] n) +{ + int result = int.min; + for (uint i = 0; i < n.length; i++) + if (n[i] > result) + result = n[i]; + return result; +} + +unittest +{ + static int[3] n = [ 0, 2, -1 ]; + assert(max(n) == 2); +} + +long max(long[] n) +{ + long result = long.min; + for (uint i = 0; i < n.length; i++) + if (n[i] > result) + result = n[i]; + return result; +} + +unittest +{ + static long[3] n = [ 0, 2, -1 ]; + assert(max(n) == 2); +} + +extended max(extended[] n) +{ + extended result = extended.min; + for (uint i = 0; i < n.length; i++) + if (n[i] > result) + result = n[i]; + return result; +} + +unittest +{ + static extended[3] n = [ 0.0, 2.0, -1.0 ]; + assert(feq(max(n), 2)); +} + +int max(int a, int b) +{ + return a > b ? a : b; +} + +unittest +{ + assert(max(1, 2) == 2); +} + +long max(long a, long b) +{ + return a > b ? a : b; +} + +unittest +{ + assert(max(1L, 2L) == 2); +} + +extended max(extended a, extended b) +{ + return a > b ? a : b; +} + +unittest +{ + assert(feq(max(1.0L, 2.0L), 2.0L)); +} + +/************************************* + * Arccosine + */ + +extended acos(extended x) +{ + return atan2(intrinsic.sqrt(1 - x * x), x); +} + +unittest +{ + assert(feq(acos(0.5), PI / 3)); +} + +/************************************* + * Arcsine + */ + + +extended asin(extended x) +{ + return atan2(x, intrinsic.sqrt(1 - x * x)); +} + +unittest +{ + assert(feq(asin(0.5), PI / 6)); +} + + +/************************************* + * Arctangent + */ + +extended atan(extended x) +{ + asm + { + fld x; + fld1; + fpatan; + fwait; + } +} + +unittest +{ + assert(feq(atan(intrinsic.sqrt(3)), PI / 3)); +} + + +/************************************* + * Arctangent y/x + */ + +extended atan2(extended y, extended x) +{ + asm + { + fld y; + fld x; + fpatan; + fwait; + } +} + +unittest +{ + assert(feq(atan2(1, intrinsic.sqrt(3)), PI / 6)); +} + + +/************************************* + * Arccotangent + */ + +extended acot(extended x) +{ + return tan(1.0 / x); +} + +unittest +{ + assert(feq(acot(cot(0.000001)), 0.000001)); +} + +/************************************* + * Arcsecant + */ + +extended asec(extended x) +{ + return intrinsic.cos(1.0 / x); +} + + +/************************************* + * Arccosecant + */ + +extended acosec(extended x) +{ + return intrinsic.sin(1.0 / x); +} + +/************************************* + * Tangent + */ + +extended tan(extended x) +{ + asm + { + fld x; + fptan; + fstp ST(0); + fwait; + } +} + +unittest +{ + assert(feq(tan(PI / 3), intrinsic.sqrt(3))); +} + +/************************************* + * Cotangent + */ + +extended cot(extended x) +{ + asm + { + fld x; + fptan; + fdivrp; + fwait; + } +} + +unittest +{ + assert(feq(cot(PI / 6), intrinsic.sqrt(3))); +} + +/************************************* + * Secant + */ + +extended sec(extended x) +{ + asm + { + fld x; + fcos; + fld1; + fdivrp; + fwait; + } +} + + +/************************************* + * Cosecant + */ + +extended cosec(extended x) +{ + asm + { + fld x; + fsin; + fld1; + fdivrp; + fwait; + } +} + +/************************************* + * Hypotenuse of right triangle + */ + +extended hypot(extended x, extended y) +{ + asm + { + fld y; + fabs; + fld x; + fabs; + fcom; + fnstsw AX; + test AH, 0x45; + jnz _1; + fxch ST(1); +_1: fldz; + fcomp; + fnstsw AX; + test AH, 0x40; + jz _2; + fstp ST(0); + jmp _3; +_2: fdiv ST, ST(1); + fmul ST, ST(0); + fld1; + fadd; + fsqrt; + fmul; +_3: fwait; + } +} + +unittest +{ + assert(feq(hypot(3, 4), 5)); +} + +/********************************************* + * Extract mantissa and exponent from float + */ + +extended frexp(extended x, out int exponent) +{ + asm + { + fld x; + mov EDX, exponent; + mov dword ptr [EDX], 0; + ftst; + fstsw AX; + fwait; + sahf; + jz done; + fxtract; + fxch; + fistp dword ptr [EDX]; + fld1; + fchs; + fxch; + fscale; + inc dword ptr [EDX]; + fstp ST(1); +done: + fwait; + } +} + +unittest +{ + int exponent; + extended mantissa = frexp(123.456, exponent); + assert(feq(mantissa * pow(2, exponent), 123.456)); +} + +/********************************************** + * Make a float out of mantissa and exponent + */ + +extended ldexp(extended x, int exponent) +{ + asm + { + fild exponent; + fld x; + fscale; + fstp ST(1); + fwait; + } +} + +unittest +{ + assert(feq(ldexp(666, 10), 666 * 1024)); +} + +/************************************* + * Round to nearest int > x + */ + +long ceil(extended x) +{ + return frac(x) > 0 ? trunc(x) + 1 : trunc(x); +} + +unittest +{ + assert(ceil(+123.456) == +124); + assert(ceil(-123.456) == -123); +} + +/************************************* + * Round to nearest int < x + */ + +long floor(extended x) +{ + return frac(x) < 0 ? trunc(x) - 1 : trunc(x); +} + +unittest +{ + assert(floor(+123.456) == +123); + assert(floor(-123.456) == -124); +} + +/************************************* + * Base 10 logarithm + */ + +extended log10(extended x) +{ + asm + { + fldlg2; + fld x; + fyl2x; + fwait; + } +} + +unittest +{ + assert(feq(log10(1000), 3)); +} + +/************************************* + * Base 2 logarithm + */ + +extended log2(extended x) +{ + asm + { + fld1; + fld x; + fyl2x; + fwait; + } +} + +unittest +{ + assert(feq(log2(1024), 10)); +} + +/************************************* + * Natural logarithm + */ + +extended log(extended x) +{ + asm + { + fldln2; + fld x; + fyl2x; + fwait; + } +} + +unittest +{ + assert(feq(log(E), 1)); +} + +/************************************* + * Natural logarithm of (x + 1) + */ + +extended log1p(extended x) +{ + asm + { + fldln2; + fld x; + fyl2xp1; + fwait; + } +} + +unittest +{ + assert(feq(log1p(E - 1), 1)); +} + +/************************************* + * Logarithm + */ + +extended log(extended x, extended base) +{ + asm + { + fld1; + fld x; + fyl2x; + fld1; + fld base; + fyl2x; + fdiv; + fwait; + } +} + +unittest +{ + assert(feq(log(81, 3), 4)); +} + +/************************************* + * (base + 1) logarithm of x + */ + +extended log1p(extended x, extended base) +{ + asm + { + fld1; + fld x; + fyl2x; + fld1; + fld base; + fyl2xp1; + fdiv; + fwait; + } +} + +unittest +{ + assert(feq(log1p(81, 3 - 1), 4)); +} + +/************************************* + * Exponent + */ + +extended exp(extended x) +{ + asm + { + fld x; + fldl2e; + fmul; + fld ST(0); + frndint; + fsub ST(1), ST; + fxch ST(1); + f2xm1; + fld1; + fadd; + fscale; + fstp ST(1); + fwait; + } +} + +unittest +{ + assert(feq(exp(3), E * E * E)); +} + +/************************************* + * Base to exponent + */ + +extended pow(extended base, extended exponent) +{ + return exp(exponent * log(base)); +} + +unittest +{ + assert(feq(pow(2, 10), 1024)); +} + +/************************************* + * Hyperbolic cosine + */ + +extended cosh(extended x) +{ + extended z = exp(x) / 2; + return z + 0.25 / z; +} + +unittest +{ + assert(feq(cosh(1), (E + 1.0 / E) / 2)); +} + +/************************************* + * Hyperbolic sine + */ + +extended sinh(extended x) +{ + extended z = exp(x) / 2; + return z - 0.25 / z; +} + +unittest +{ + assert(feq(sinh(1), (E - 1.0 / E) / 2)); +} + +/************************************* + * Hyperbolic tangent + */ + +private extended tanh_domain; + +extended tanh(extended x) +{ + if (x > tanh_domain) + return 1; + else if (x < -tanh_domain) + return -1; + else + { + extended z = sqr(exp(x)); + return (z - 1) / (z + 1); + } +} + +unittest +{ + assert(feq(tanh(1), sinh(1) / cosh(1))); +} + +/************************************* + * Hyperbolic cotangent + */ + +extended coth(extended x) +{ + return 1 / tanh(x); +} + +unittest +{ + assert(feq(coth(1), cosh(1) / sinh(1))); +} + +/************************************* + * Hyperbolic secant + */ + +extended sech(extended x) +{ + return 1 / cosh(x); +} + +/************************************* + * Hyperbolic cosecant + */ + +extended cosech(extended x) +{ + return 1 / sinh(x); +} + +/************************************* + * Hyperbolic arccosine + */ + +extended acosh(extended x) +{ + if (x <= 1) + return 0; + else if (x > 1.0e10) + return log(2) + log(x); + else + return log(x + intrinsic.sqrt((x - 1) * (x + 1))); +} + +unittest +{ + assert(acosh(0.5) == 0); + assert(feq(acosh(cosh(3)), 3)); +} + +/************************************* + * Hyperbolic arcsine + */ + +extended asinh(extended x) +{ + if (!x) + return 0; + else if (x > 1.0e10) + return log(2) + log(1.0e10); + else if (x < -1.0e10) + return -log(2) - log(1.0e10); + else + { + extended z = x * x; + return x > 0 ? + log1p(x + z / (1.0 + intrinsic.sqrt(1.0 + z))) : + -log1p(-x + z / (1.0 + intrinsic.sqrt(1.0 + z))); + } +} + +unittest +{ + assert(asinh(0) == 0); + assert(feq(asinh(sinh(3)), 3)); +} + +/************************************* + * Hyperbolic arctangent + */ + +extended atanh(extended x) +{ + if (!x) + return 0; + else + { + if (x >= 1) + return extended.max; + else if (x <= -1) + return -extended.max; + else + return x > 0 ? + 0.5 * log1p((2.0 * x) / (1.0 - x)) : + -0.5 * log1p((-2.0 * x) / (1.0 + x)); + } +} + +unittest +{ + assert(atanh(0) == 0); + assert(feq(atanh(tanh(0.5)), 0.5)); +} + +/************************************* + * Hyperbolic arccotangent + */ + +extended acoth(extended x) +{ + return 1 / acot(x); +} + +unittest +{ + assert(feq(acoth(coth(0.01)), 100)); +} + +/************************************* + * Hyperbolic arcsecant + */ + +extended asech(extended x) +{ + return 1 / asec(x); +} + +/************************************* + * Hyperbolic arccosecant + */ + +extended acosech(extended x) +{ + return 1 / acosec(x); +} + +/************************************* + * Convert string to float + */ + +extended atof(char[] s) +{ + if (!s.length) + return extended.nan; + extended result = 0; + uint i = 0; + while (s[i] == "\t" || s[i] == " ") + if (++i >= s.length) + return extended.nan; + bit neg = false; + if (s[i] == "-") + { + neg = true; + i++; + } + else if (s[i] == "+") + i++; + if (i >= s.length) + return extended.nan; + bit hex; + if (s[s.length - 1] == "h") + { + hex = true; + s.length = s.length - 1; + } + else if (i + 1 < s.length && s[i] == "0" && s[i+1] == "x") + { + hex = true; + i += 2; + if (i >= s.length) + return extended.nan; + } + else + hex = false; + while (s[i] != ".") + { + if (hex) + { + if ((s[i] == "p" || s[i] == "P")) + break; + result *= 0x10; + } + else + { + if ((s[i] == "e" || s[i] == "E")) + break; + result *= 10; + } + if (s[i] >= "0" && s[i] <= "9") + result += s[i] - "0"; + else if (hex) + { + if (s[i] >= "a" && s[i] <= "f") + result += s[i] - "a" + 10; + else if (s[i] >= "A" && s[i] <= "F") + result += s[i] - "A" + 10; + else + return extended.nan; + } + else + return extended.nan; + if (++i >= s.length) + goto done; + } + if (s[i] == ".") + { + if (++i >= s.length) + goto done; + ulong k = 1; + while (true) + { + if (hex) + { + if ((s[i] == "p" || s[i] == "P")) + break; + result *= 0x10; + } + else + { + if ((s[i] == "e" || s[i] == "E")) + break; + result *= 10; + } + k *= (hex ? 0x10 : 10); + if (s[i] >= "0" && s[i] <= "9") + result += s[i] - "0"; + else if (hex) + { + if (s[i] >= "a" && s[i] <= "f") + result += s[i] - "a" + 10; + else if (s[i] >= "A" && s[i] <= "F") + result += s[i] - "A" + 10; + else + return extended.nan; + } + else + return extended.nan; + if (++i >= s.length) + { + result /= k; + goto done; + } + } + result /= k; + } + if (++i >= s.length) + return extended.nan; + bit eneg = false; + if (s[i] == "-") + { + eneg = true; + i++; + } + else if (s[i] == "+") + i++; + if (i >= s.length) + return extended.nan; + int e = 0; + while (i < s.length) + { + e *= 10; + if (s[i] >= "0" && s[i] <= "9") + e += s[i] - "0"; + else + return extended.nan; + i++; + } + if (eneg) + e = -e; + result *= pow(hex ? 2 : 10, e); +done: + return neg ? -result : result; +} + +unittest +{ + assert(feq(atof("123"), 123)); + assert(feq(atof("+123"), +123)); + assert(feq(atof("-123"), -123)); + assert(feq(atof("123e2"), 12300)); + assert(feq(atof("123e+2"), 12300)); + assert(feq(atof("123e-2"), 1.23)); + assert(feq(atof("123."), 123)); + assert(feq(atof("123.E-2"), 1.23)); + assert(feq(atof(".456"), .456)); + assert(feq(atof("123.456"), 123.456)); + assert(feq(atof("1.23456E+2"), 123.456)); + assert(feq(atof("1A2h"), 1A2h)); + assert(feq(atof("1a2h"), 1a2h)); + assert(feq(atof("0x1A2"), 0x1A2)); + assert(feq(atof("0x1a2p2"), 0x1a2p2)); + assert(feq(atof("0x1a2p+2"), 0x1a2p+2)); + assert(feq(atof("0x1a2p-2"), 0x1a2p-2)); + assert(feq(atof("0x1A2.3B4"), 0x1A2.3B4p0)); + assert(feq(atof("0x1a2.3b4P2"), 0x1a2.3b4P2)); +} + +/************************************* + * Convert float to string + */ + +char[] toString(extended x) +{ + char[1024] buffer; + char* p = buffer; + uint psize = buffer.length; + int count; + while (true) + { + version(Win32) + { + count = _snprintf(p, psize, "%Lg", x); + if (count != -1) + break; + psize *= 2; + } + else version(linux) + { + count = snprintf(p, psize, "%Lg", x); + if (count == -1) + psize *= 2; + else if (count >= psize) + psize = count + 1; + else + break; + } + p = cast(char*) alloca(psize); + } + return p[0 .. count]; +} + +unittest +{ + assert(!cmp(toString(123.456), "123.456")); +} + +/************************************* + * Static constructor + */ + +static this() +{ + tanh_domain = log(extended.max) / 2; +} diff --git a/memset.c b/memset.c new file mode 100644 index 000000000..54ae25b65 --- /dev/null +++ b/memset.c @@ -0,0 +1,54 @@ + +#include + +short *_memset16(short *p, short value, int count) +{ + short *pstart = p; + short *ptop; + + for (ptop = &p[count]; p < ptop; p++) + *p = value; + return pstart; +} + +int *_memset32(int *p, int value, int count) +{ + int *pstart = p; + int *ptop; + + for (ptop = &p[count]; p < ptop; p++) + *p = value; + return pstart; +} + +long long *_memset64(long long *p, long long value, int count) +{ + long long *pstart = p; + long long *ptop; + + for (ptop = &p[count]; p < ptop; p++) + *p = value; + return pstart; +} + +long double *_memset80(long double *p, long double value, int count) +{ + long double *pstart = p; + long double *ptop; + + for (ptop = &p[count]; p < ptop; p++) + *p = value; + return pstart; +} + +void *_memsetn(void *p, void *value, int count, int sizelem) +{ void *pstart = p; + int i; + + for (i = 0; i < count; i++) + { + memcpy(p, value, sizelem); + p = (void *)((char *)p + sizelem); + } + return pstart; +} diff --git a/moduleinit.d b/moduleinit.d index 3d31753b7..e4db5c015 100644 --- a/moduleinit.d +++ b/moduleinit.d @@ -26,7 +26,7 @@ class ModuleCtorError : Exception { this(ModuleInfo m) { - msg = "circular initialization dependency with module " ~ m.name; + super("circular initialization dependency with module " ~ m.name); } } @@ -49,10 +49,10 @@ extern (C) void _moduleCtor() _fatexit(&_moduleDtor); _moduleinfo_dtors = new ModuleInfo[_moduleinfo_array.length]; - _moduleCtor2(_moduleinfo_array); + _moduleCtor2(_moduleinfo_array, 0); } -void _moduleCtor2(ModuleInfo[] mi) +void _moduleCtor2(ModuleInfo[] mi, int skip) { //printf("_moduleCtor2(): %d modules\n", mi.length); for (uint i = 0; i < mi.length; i++) @@ -66,10 +66,13 @@ void _moduleCtor2(ModuleInfo[] mi) if (m.ctor || m.dtor) { if (m.flags & MIctorstart) + { if (skip) + continue; throw new ModuleCtorError(m); + } m.flags |= MIctorstart; - _moduleCtor2(m.importedModules); + _moduleCtor2(m.importedModules, 0); if (m.ctor) (*m.ctor)(); m.flags &= ~MIctorstart; @@ -82,7 +85,7 @@ void _moduleCtor2(ModuleInfo[] mi) else { m.flags |= MIctordone; - _moduleCtor2(m.importedModules); + _moduleCtor2(m.importedModules, 1); } } } diff --git a/modulo.c b/modulo.c deleted file mode 100644 index f0a68a7bb..000000000 --- a/modulo.c +++ /dev/null @@ -1,39 +0,0 @@ - -long double _modulo(long double x, long double y) -{ short sw; - - __asm - { - fld tbyte ptr y - fld tbyte ptr x // ST = x, ST1 = y -FM1: // We don't use fprem1 because for some inexplicable - // reason we get -5 when we do _modulo(15, 10) - fprem // ST = ST % ST1 - fstsw word ptr sw - fwait - mov AH,byte ptr sw+1 // get msb of status word in AH - sahf // transfer to flags - jp FM1 // continue till ST < ST1 - fstp ST(1) // leave remainder on stack - } -} - -/************************* Test **********************/ - -#if 0 - -#include - -extern double _fmod87(double x, double y); - -void main() -{ - double x = 15; - double y = 10; - double z = _modulo(x, y); - printf("z = %g\n", z); - z = _fmod87(x, y); - printf("z = %g\n", z); -} - -#endif diff --git a/monitor.c b/monitor.c index 9e77deeab..1bf6a25f0 100644 --- a/monitor.c +++ b/monitor.c @@ -35,7 +35,7 @@ void _STD_monitor_staticdtor() void _d_monitorenter(Object *h) { - //printf("_d_monitorenter(%p)\n", h); + //printf("_d_monitorenter(%p), %p\n", h, h->monitor); if (!h->monitor) { CRITICAL_SECTION *cs; @@ -52,7 +52,9 @@ void _d_monitorenter(Object *h) if (cs) // if we didn't use it free(cs); } + //printf("-_d_monitorenter(%p)\n", h); EnterCriticalSection((CRITICAL_SECTION *)h->monitor); + //printf("-_d_monitorenter(%p)\n", h); } void _d_monitorexit(Object *h) diff --git a/new.cpp b/new.cpp deleted file mode 100644 index 9a9776432..000000000 --- a/new.cpp +++ /dev/null @@ -1,165 +0,0 @@ - -// Storage allocation - -#include -#include -#include -#include - -#define __STDC__ 1 -#include - -#include "mars.h" - -GC gc; - -//extern "C" void *_atopsp; - -void new_finalizer(void *p, void *dummy); - -extern "C" -{ - -void gc_init() -{ - gc.init(); - //gc.setStackBottom(_atopsp); - gc.scanStaticData(); -} - -void gc_term() -{ - gc.fullcollect(); -} - -Object * __ddecl __d_newclass(ClassInfo *ci) -{ - void *p; - - //printf("__d_newclass(ci = %p)\n", ci); - if (ci->flags & 1) // if COM object - { - p = (Object *)malloc(ci->initlen); - if (!p) - _d_OutOfMemory(); - } - else - { - p = gc.malloc(ci->initlen); - //printf(" p = %p\n", p); - if (!p) - _d_OutOfMemory(); - gc.setFinalizer(p, new_finalizer); - } - -#if 0 - printf("p = %p\n", p); - printf("ci = %p, ci->init = %p, len = %d\n", ci, ci->init, ci->initlen); - printf("vptr = %p\n", *(void **)ci->init); - printf("vtbl[0] = %p\n", (*(void ***)ci->init)[0]); - printf("vtbl[1] = %p\n", (*(void ***)ci->init)[1]); - printf("init[0] = %x\n", ((unsigned *)ci->init)[0]); - printf("init[1] = %x\n", ((unsigned *)ci->init)[1]); - printf("init[2] = %x\n", ((unsigned *)ci->init)[2]); - printf("init[3] = %x\n", ((unsigned *)ci->init)[3]); - printf("init[4] = %x\n", ((unsigned *)ci->init)[4]); -#endif - - - // Initialize it - p = memcpy(p, ci->init, ci->initlen); - - //printf("initialization done\n"); - return (Object *)p; -} - -typedef void __ddecl (*fp_t)(Object *); // generic function pointer - -void __ddecl __d_delclass(Object **p) -{ - if (*p) - { -#if 0 - ClassInfo ***pc = (ClassInfo ***)*p; - if (*pc) - { - ClassInfo *c = **pc; - - if (c->destructor) - { - fp_t fp = (fp_t)c->destructor; - (*fp)(*p); // call destructor - } - *pc = NULL; // zero vptr - } -#endif - gc.free(*p); - *p = NULL; - } -} - -unsigned long long __ddecl __d_new(unsigned length, unsigned size) -{ - void *p; - unsigned long long result; - - //printf("__d_new(length = %d, size = %d)\n", length, size); - if (length == 0 || size == 0) - result = 0; - else - { - p = gc.malloc(length * size); - if (!p) - _d_OutOfMemory(); - //printf(" p = %p\n", p); - memset(p, 0, length * size); - result = (unsigned long long)length + ((unsigned long long)(unsigned)p << 32); - } - return result; -} - -struct Array -{ - unsigned length; - void *data; -}; - -// Perhaps we should get a a size argument like __d_new(), so we -// can zero out the array? - -void __ddecl __d_delarray(struct Array *p) -{ - if (p) - { - assert(!p->length || p->data); - if (p->data) - gc.free(p->data); - p->data = NULL; - p->length = 0; - } -} - - -} - -void new_finalizer(void *p, void *dummy) -{ - //printf("new_finalizer(p = %p)\n", p); - ClassInfo ***pc = (ClassInfo ***)p; - if (*pc) - { - ClassInfo *c = **pc; - - do - { - if (c->destructor) - { - fp_t fp = (fp_t)c->destructor; - (*fp)((Object *)p); // call destructor - } - c = c->baseClass; - } while (c); - *pc = NULL; // zero vptr - } -} - diff --git a/obj.d b/obj.d new file mode 100644 index 000000000..c421160b5 --- /dev/null +++ b/obj.d @@ -0,0 +1,28 @@ + + +// Copyright (c) 2002 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com + +extern (C): + +/******************************** + * Compiler helper for operator == for class objects. + */ + +int _d_obj_eq(Object o1, Object o2) +{ + return o1 === o2 || (o1 && o1.eq(o2)); +} + + +/******************************** + * Compiler helper for operator <, <=, >, >= for class objects. + */ + +int _d_obj_cmp(Object o1, Object o2) +{ + return o1.cmp(o2); +} + diff --git a/object.d b/object.d index b84ffe930..63fe24964 100644 --- a/object.d +++ b/object.d @@ -20,7 +20,12 @@ class Object int cmp(Object o) { - return (char *)(void *)this - (char *)(void *)o; + return (int)(void *)this - (int)(void *)o; + } + + int eq(Object o) + { + return this === o; } } @@ -44,6 +49,26 @@ class ClassInfo : Object // 1: // IUnknown } +class TypeInfo +{ + uint getHash(void *p) { return (uint)p; } + int equals(void *p1, void *p2) { return p1 == p2; } + int compare(void *p1, void *p2) { return 0; } + int tsize() { return 0; } + void swap(void *p1, void *p2) + { + int i; + int n = tsize(); + for (i = 0; i < n; i++) + { byte t; + + t = ((byte *)p1)[i]; + ((byte *)p1)[i] = ((byte *)p2)[i]; + ((byte *)p2)[i] = t; + } + } +} + class Exception : Object { char[] msg; @@ -77,27 +102,3 @@ class Error : Exception } } -extern (C) -{ - // These are helper functions internal to the D runtime library (phobos.lib) - void*[] _aaRehashAh(void*[] paa); - char[][] _aaKeys8(void*[] paa); - int[] _aaValues8_4(void*[] paa); -} - -int[char[]] rehash(int[char[]] aa) -{ - return (int[char[]]) _aaRehashAh((void*[])aa); -} - -char[][] keys(int[char[]] aa) -{ - return (char[][]) _aaKeys8((void*[])aa); -} - -int[] values(int[char[]] aa) -{ - return (int[]) _aaValues8_4((void*[])aa); -} - - diff --git a/path.d b/path.d index 9c58a2054..65f8a6eb3 100644 --- a/path.d +++ b/path.d @@ -104,17 +104,33 @@ char[] getBaseName(char[] fullname) version(Win32) { if (fullname[i - 1] == ':' || fullname[i - 1] == '\') - return null; + break; } version(linux) { if (fullname[i - 1] == '/') - return null; + break; } } return fullname[i .. fullname.length]; } +unittest +{ + debug(path) printf("path.getBaseName.unittest\n"); + int i; + char[] result; + + result = getBaseName('d:\path\foo.bat'); + //printf("result = '%.*s'\n", result); + i = cmp(result, "foo.bat"); + assert(i == 0); + + result = getBaseName('a\b'); + i = cmp(result, "b"); + assert(i == 0); +} + /************************** * Get directory name. diff --git a/qsort.d b/qsort.d new file mode 100644 index 000000000..70ff5ff66 --- /dev/null +++ b/qsort.d @@ -0,0 +1,157 @@ +/* + Copyright Prototronics, 1987 + Totem Lake P.O. 8117 + Kirkland, Washington 98034 + (206) 820-1972 + Licensed to Digital Mars. + + June 11, 1987 from Ray Gardner's + Denver, Colorado) public domain version +*/ + + +/* +** Sorts an array starting at base, of length nbr_elements, each +** element of size width_bytes, ordered via compare_function; which +** is called as (*comp_fp)(ptr_to_element1, ptr_to_element2) +** and returns < 0 if element1 < element2, 0 if element1 = element2, +** > 0 if element1 > element2. Most of the refinements are due to +** R. Sedgewick. See "Implementing Quicksort Programs", Comm. ACM, +** Oct. 1978, and Corrigendum, Comm. ACM, June 1979. +*/ + +//debug=qsort; // uncomment to turn on debugging printf's + +import c.stdio; +import c.stdlib; +import string; +import outofmemory; + +struct Array +{ + int length; + void *ptr; +} + + +private const int _maxspan = 7; // subarrays of _maxspan or fewer elements + // will be sorted by a simple insertion sort + +/* Adjust _maxspan according to relative cost of a swap and a compare. Reduce +_maxspan (not less than 1) if a swap is very expensive such as when you have +an array of large structures to be sorted, rather than an array of pointers to +structures. The default value is optimized for a high cost for compares. */ + + +extern (C) Array _adSort(Array a, TypeInfo ti) +{ + byte* base; + byte*[40] stack; // stack + byte** sp; // stack pointer + byte* i, j, limit; // scan and limit pointers + uint thresh; // size of _maxspan elements in bytes + uint width = ti.tsize(); + + base = (byte *)a.ptr; + thresh = _maxspan * width; // init threshold + sp = stack; // init stack pointer + limit = base + a.length * width; // pointer past end of array + while (1) // repeat until done then return + { + while (limit - base > thresh) // if more than _maxspan elements + { + //swap middle, base + ti.swap(((uint)(limit - base) >> 1) - + ((((uint)(limit - base) >> 1)) % width) + base, base); + + i = base + width; // i scans from left to right + j = limit - width; // j scans from right to left + + if (ti.compare(i, j) > 0) // Sedgewick's + ti.swap(i, j); // three-element sort + if (ti.compare(base, j) > 0) // sets things up + ti.swap(base, j); // so that + if (ti.compare(i, base) > 0) // *i <= *base <= *j + ti.swap(i, base); // *base is the pivot element + + while (1) + { + do // move i right until *i >= pivot + i += width; + while (ti.compare(i, base) < 0); + do // move j left until *j <= pivot + j -= width; + while (ti.compare(j, base) > 0); + if (i > j) // break loop if pointers crossed + break; + ti.swap(i, j); // else swap elements, keep scanning + } + ti.swap(base, j); // move pivot into correct place + if (j - base > limit - i) // if left subarray is larger... + { + sp[0] = base; // stack left subarray base + sp[1] = j; // and limit + base = i; // sort the right subarray + } + else // else right subarray is larger + { + sp[0] = i; // stack right subarray base + sp[1] = limit; // and limit + limit = j; // sort the left subarray + } + sp += 2; // increment stack pointer + assert(sp < (byte**)stack + stack.length); + } + + // Insertion sort on remaining subarray + i = base + width; + while (i < limit) + { + j = i; + while (j > base && ti.compare(j - width, j) > 0) + { + ti.swap(j - width, j); + j -= width; + } + i += width; + } + + if (sp > stack) // if any entries on stack... + { + sp -= 2; // pop the base and limit + base = sp[0]; + limit = sp[1]; + } + else // else stack empty, all done + return a; + } +} + + +unittest +{ + debug(qsort) printf("array.sort.unittest()\n"); + + int a[] = new int[10]; + + a[0] = 23; + a[1] = 1; + a[2] = 64; + a[3] = 5; + a[4] = 6; + a[5] = 5; + a[6] = 17; + a[7] = 3; + a[8] = 0; + a[9] = -1; + + a.sort; + + for (int i = 0; i < a.length - 1; i++) + { + //printf("i = %d", i); + //printf(" %d %d\n", a[i], a[i + 1]); + assert(a[i] <= a[i + 1]); + } +} + diff --git a/regexp.d b/regexp.d index 88238d80b..6aaefc82e 100644 --- a/regexp.d +++ b/regexp.d @@ -200,7 +200,7 @@ void compile(tchar[] pattern, tchar[] attributes) if (re_nsub > oldre_nsub) { - if (pmatch == &gmatch) + if (pmatch === &gmatch) pmatch = null; pmatch.length = re_nsub + 1; } @@ -470,7 +470,10 @@ public tchar[][] exec() result = new tchar[pmatch.length][]; for (int i = 0; i < pmatch.length; i++) { - result[i] = input[pmatch[i].rm_so .. pmatch[i].rm_eo]; + if (pmatch[i].rm_so == pmatch[i].rm_eo) + result[i] = null; + else + result[i] = input[pmatch[i].rm_so .. pmatch[i].rm_eo]; } return result; @@ -1795,6 +1798,9 @@ int parseRange() } rs = RS.start; continue; + + default: + break; } c2 = escape(); goto Lrange; @@ -2390,7 +2396,8 @@ static tchar[] replace3(tchar[] format, tchar[] input, regmatch_t[] pmatch) break; Lstring: - buf.write(input[rm_so .. rm_eo]); + if (rm_so != rm_eo) + buf.write(input[rm_so .. rm_eo]); break; default: diff --git a/stdint.d b/stdint.d new file mode 100644 index 000000000..ff3c87b3b --- /dev/null +++ b/stdint.d @@ -0,0 +1,45 @@ + + +/* Exact sizes */ + +alias byte int8_t; +alias ubyte uint8_t; +alias short int16_t; +alias ushort uint16_t; +alias int int32_t; +alias uint uint32_t; +alias long int64_t; +alias ulong uint64_t; + +/* At least sizes */ + +alias byte int_least8_t; +alias ubyte uint_least8_t; +alias short int_least16_t; +alias ushort uint_least16_t; +alias int int_least32_t; +alias uint uint_least32_t; +alias long int_least64_t; +alias ulong uint_least64_t; + +/* Fastest minimum width sizes */ + +alias byte int_fast8_t; +alias ubyte uint_fast8_t; +alias int int_fast16_t; +alias uint uint_fast16_t; +alias int int_fast32_t; +alias uint uint_fast32_t; +alias long int_fast64_t; +alias ulong uint_fast64_t; + +/* Integer pointer holders */ + +alias int intptr_t; +alias uint uintptr_t; + +/* Greatest width integer types */ + +alias long intmax_t; +alias ulong uintmax_t; + diff --git a/stream.d b/stream.d index f794b6f7b..e7d54bbe2 100644 --- a/stream.d +++ b/stream.d @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 + * Copyright (c) 2001, 2002 * Pavel "EvilOne" Minayev * * Permission to use, copy, modify, distribute and sell this software @@ -11,12 +11,11 @@ * "as is" without express or implied warranty. */ -import windows; -import string; +module stream; // generic Stream error, base class for all // other Stream exceptions -class StreamError: Exception +class StreamError: Error { this(char[] msg) { super(msg); } } @@ -39,12 +38,27 @@ class SeekError: StreamError this(char[] msg) { super(msg); } } +// seek whence... +enum SeekPos +{ + Set, + Current, + End +} + // base class for all streams; not really abstract, -// but its instances will do nothing +// but its instances will do nothing useful class Stream { - // seek from... - enum { set, current, end } + import string, crc32, c.stdlib, c.stdio; + + // for compatibility + deprecated enum: SeekPos + { + set = SeekPos.Set, + current = SeekPos.Current, + end = SeekPos.End + } // stream abilities bit readable = false; @@ -55,15 +69,7 @@ class Stream // reads block of data of specified size, // returns actual number of bytes read - uint readBlock(void* buffer, uint size) - in - { - assert(readable); - } - body - { - return 0; - } + abstract uint readBlock(void* buffer, uint size); // reads block of data of specified size, // throws ReadError on error @@ -73,6 +79,13 @@ class Stream throw new ReadError("not enough data in stream"); } + // reads block of data big enough to fill the given + // array, returns actual number of bytes read + uint read(ubyte[] buffer) + { + return readBlock(buffer, buffer.length); + } + // read a single value of desired type, // throw ReadError on error void read(out byte x) { readExact(&x, x.size); } @@ -81,28 +94,106 @@ class Stream void read(out ushort x) { readExact(&x, x.size); } void read(out int x) { readExact(&x, x.size); } void read(out uint x) { readExact(&x, x.size); } + void read(out long x) { readExact(&x, x.size); } + void read(out ulong x) { readExact(&x, x.size); } + void read(out float x) { readExact(&x, x.size); } + void read(out double x) { readExact(&x, x.size); } + void read(out extended x) { readExact(&x, x.size); } + void read(out imaginary x) { readExact(&x, x.size); } + void read(out complex x) { readExact(&x, x.size); } void read(out char x) { readExact(&x, x.size); } void read(out wchar x) { readExact(&x, x.size); } - - // reads a line, terminated by either CR/LF, LF or EOF + + // reads a string, written earlier by write() + void read(out char[] s) + { + int len; + read(len); + s = readString(len); + } + + // reads a Unicode string, written earlier by write() + void read(out wchar[] s) + { + int len; + read(len); + s = readStringW(len); + } + + // reads a line, terminated by either CR, LF, CR/LF, or EOF char[] readLine() { char[] result; - char c; try { - read(c); - while (c != 10) + char c = getc(); + while (readable) { - if (c != 13) - result ~= c; - read(c); + switch (c) + { + case "\r": + { + c = getc(); + if (c != "\n") + ungetc(c); + } + + case "\n": + return result; + + default: + result ~= c; + } + c = getc(); } } - finally + catch (ReadError e) { - return result; + // either this is end of stream, which is okay, + // or something bad occured while reading + if (!eof()) + throw e; } + return result; + } + + // reads a Unicode line, terminated by either CR, LF, CR/LF, + // or EOF; pretty much the same as the above, working with + // wchars rather than chars + wchar[] readLineW() + { + wchar[] result; + try + { + wchar c = getcw(); + while (readable) + { + switch (c) + { + case "\r": + { + c = getcw(); + if (c != "\n") + ungetcw(c); + } + + case "\n": + return result; + + default: + result ~= c; + } + c = getcw(); + } + } + catch (ReadError e) + { + // either this is end of stream, which is okay, + // or something bad occured while reading + if (!eof()) + throw e; + } + return result; } // reads a string of given length, throws @@ -114,17 +205,415 @@ class Stream return result; } + // reads a Unicode string of given length, throws + // ReadError on error + wchar[] readStringW(uint length) + { + wchar[] result = new wchar[length]; + readExact(result, result.length * wchar.size); + return result; + } + + // unget buffer + private wchar[] unget; + + // reads and returns next character from the stream, + // handles characters pushed back by ungetc() + char getc() + { + char c; + if (unget.length) + { + c = unget[unget.length - 1]; + unget.length = unget.length - 1; + } + else + read(c); + return c; + } + + // reads and returns next Unicode character from the + // stream, handles characters pushed back by ungetc() + wchar getcw() + { + wchar c; + if (unget.length) + { + c = unget[unget.length - 1]; + unget.length = unget.length - 1; + } + else + read(c); + return c; + } + + // pushes back character c into the stream; only has + // effect on further calls to getc() and getcw() + char ungetc(char c) + { + unget ~= c; + return c; + } + + // pushes back Unicode character c into the stream; only + // has effect on further calls to getc() and getcw() + wchar ungetcw(wchar c) + { + unget ~= c; + return c; + } + + int vscanf(char[] fmt, va_list args) + { + void** arg = cast(void**) args; + int count = 0, i = 0; + char c = getc(); + while (i < fmt.length) + { + if (fmt[i] == "%") // a field + { + i++; + bit suppress = false; + if (fmt[i] == "*") // suppress assignment + { + suppress = true; + i++; + } + // read field width + int width = 0; + while (isdigit(fmt[i])) + { + width = width * 10 + (fmt[i] - "0"); + i++; + } + if (width == 0) + width = -1; + // D string? + bit dstr = false; + if (fmt[i] == ".") + { + i++; + if (fmt[i] == "*") + { + dstr = true; + i++; + } + } + // read the modifier + char modifier = fmt[i]; + if (modifier == "h" || modifier == "l" || modifier == "L") + i++; + else + modifier = 0; + // check the typechar and act accordingly + switch (fmt[i]) + { + case "d": // decimal/hexadecimal/octal integer + case "D": + case "u": + case "U": + case "o": + case "O": + case "x": + case "X": + case "i": + case "I": + { + while (iswhite(c)) + { + c = getc(); + count++; + } + bit neg = false; + if (c == "-") + { + neg = true; + c = getc(); + count++; + } + else if (c == "+") + { + c = getc(); + count++; + } + char ifmt = fmt[i] | 0x20; + if (ifmt == "i") // undetermined base + { + if (c == "0") // octal or hex + { + c = getc(); + count++; + if (c == "x" || c == "X") // hex + { + ifmt = "x"; + c = getc(); + count++; + } + else // octal + ifmt = "o"; + } + else // decimal + ifmt = "d"; + } + long n = 0; + switch (ifmt) + { + case "d": // decimal + case "u": + { + while (isdigit(c) && width) + { + n = n * 10 + (c - "0"); + width--; + c = getc(); + count++; + } + } break; + + case "o": // octal + { + while (isoctdigit(c) && width) + { + n = n * 010 + (c - "0"); + width--; + c = getc(); + count++; + } + } break; + + case "x": // hexadecimal + { + while (ishexdigit(c) && width) + { + n *= 0x10; + if (isdigit(c)) + n += c - "0"; + else + n += 0xA + (c | 0x20) - "a"; + width--; + c = getc(); + count++; + } + } break; + } + if (neg) + n = -n; + // check the modifier and cast the pointer + // to appropriate type + switch (modifier) + { + case "h": // short + { + *cast(short*)*arg = n; + } break; + + case "L": // long + { + *cast(long*)*arg = n; + } break; + + default: // int + *cast(int*)*arg = n; + } + i++; + } break; + + case "f": // float + case "F": + case "e": + case "E": + case "g": + case "G": + { + while (iswhite(c)) + { + c = getc(); + count++; + } + bit neg = false; + if (c == "-") + { + neg = true; + c = getc(); + count++; + } + else if (c == "+") + { + c = getc(); + count++; + } + extended n = 0; + while (isdigit(c) && width) + { + n = n * 10 + (c - "0"); + width--; + c = getc(); + count++; + } + if (width && c == ".") + { + width--; + c = getc(); + count++; + double frac = 1; + while (isdigit(c) && width) + { + n = n * 10 + (c - "0"); + frac *= 10; + width--; + c = getc(); + count++; + } + n /= frac; + } + if (width && (c == "e" || c == "E")) + { + width--; + c = getc(); + count++; + if (width) + { + bit expneg = false; + if (c == "-") + { + expneg = true; + width--; + c = getc(); + count++; + } + else if (c == "+") + { + width--; + c = getc(); + count++; + } + extended exp = 0; + while (isdigit(c) && width) + { + exp = exp * 10 + (c - "0"); + width--; + c = getc(); + count++; + } + if (expneg) + { + while (exp--) + n /= 10; + } + else + { + while (exp--) + n *= 10; + } + } + } + if (neg) + n = -n; + // check the modifier and cast the pointer + // to appropriate type + switch (modifier) + { + case "l": // double + { + *cast(double*)*arg = n; + } break; + + case "L": // extended + { + *cast(extended*)*arg = n; + } break; + + default: // float + *cast(float*)*arg = n; + } + i++; + } break; + + case "s": // ANSI string + { + while (iswhite(c)) + { + c = getc(); + count++; + } + char[] s; + while (!iswhite(c)) + { + s ~= c; + c = getc(); + count++; + } + if (dstr) // D string (char[]) + *cast(char[]*)*arg = s; + else // C string (char*) + { + s ~= 0; + (cast(char*)*arg)[0 .. s.length] = s[]; + } + i++; + } break; + + case "c": // character(s) + { + char* s = cast(char*)*arg; + if (width < 0) + width = 1; + else + while (iswhite(c)) + { + c = getc(); + count++; + } + while (width--) + { + *(s++) = c; + c = getc(); + count++; + } + i++; + } break; + + case "n": // number of chars read so far + { + *cast(int*)*arg = count; + i++; + } break; + + default: // read character as is + goto nws; + } + arg++; + } + else if (iswhite(fmt[i])) // skip whitespace + { + while (iswhite(c)) + c = getc(); + i++; + } + else // read character as is + { +nws: + if (fmt[i] != c) + break; + c = getc(); + i++; + } + } + ungetc(c); + return count; + } + + int scanf(char[] format, ...) + { + va_list ap; + ap = cast(va_list) &format; + ap += format.size; + return vscanf(format, ap); + } + // writes block of data of specified size, // returns actual number of bytes written - uint writeBlock(void* buffer, uint size) - in - { - assert(writeable); - } - body - { - return 0; - } + abstract uint writeBlock(void* buffer, uint size); // writes block of data of specified size, // throws WriteError on error @@ -134,6 +623,13 @@ class Stream throw new WriteError("unable to write to stream"); } + // writes the given array of bytes, returns + // actual number of bytes written + uint write(ubyte[] buffer) + { + return writeBlock(buffer, buffer.length); + } + // write a single value of desired type, // throw WriteError on error void write(byte x) { writeExact(&x, x.size); } @@ -142,16 +638,56 @@ class Stream void write(ushort x) { writeExact(&x, x.size); } void write(int x) { writeExact(&x, x.size); } void write(uint x) { writeExact(&x, x.size); } + void write(long x) { writeExact(&x, x.size); } + void write(ulong x) { writeExact(&x, x.size); } + void write(float x) { writeExact(&x, x.size); } + void write(double x) { writeExact(&x, x.size); } + void write(extended x) { writeExact(&x, x.size); } + void write(imaginary x) { writeExact(&x, x.size); } + void write(complex x) { writeExact(&x, x.size); } void write(char x) { writeExact(&x, x.size); } void write(wchar x) { writeExact(&x, x.size); } + + // writes a string, together with its length + void write(char[] s) + { + write(s.length); + writeString(s); + } + + // writes a Unicode string, together with its length + void write(wchar[] s) + { + write(s.length); + writeStringW(s); + } - // writes a line, terminated by LF, - - // throws WriteError on error + // writes a line, throws WriteError on error void writeLine(char[] s) { writeString(s); - write(cast(char)"\n"); + version (Win32) + writeString("\r\n"); +/+ + else version (Mac) + writeString("\r"); ++/ + else // probably *NIX + writeString("\n"); + } + + // writes a UNICODE line, throws WriteError on error + void writeLineW(wchar[] s) + { + writeStringW(s); + version (Win32) + writeStringW("\r\n"); +/+ + else version (Mac) + writeStringW("\r"); ++/ + else // probably *NIX + writeStringW("\n"); } // writes a string, throws WriteError on error @@ -160,34 +696,137 @@ class Stream writeExact(s, s.length); } + // writes a UNICODE string, throws WriteError on error + void writeStringW(wchar[] s) + { + writeExact(s, s.length * wchar.size); + } + + // writes data to stream using vprintf() syntax, + // returns number of bytes written + uint vprintf(char[] format, va_list args) + { + // shamelessly stolen from OutBuffer, + // by Walter's permission + char[1024] buffer; + char* p = buffer; + char* f = toStringz(format); + uint psize = buffer.length; + int count; + while (true) + { + version (Win32) + { + count = _vsnprintf(p, psize, f, args); + if (count != -1) + break; + psize *= 2; + } + else version (linux) + { + count = vsnprintf(p, psize, f, args); + if (count == -1) + psize *= 2; + else if (count >= psize) + psize = count + 1; + else + break; + } + else + throw new Error("unsupported platform"); + p = cast(char*) alloca(psize); + } + writeString(p[0 .. count]); + return count; + } + + // writes data to stream using printf() syntax, + // returns number of bytes written + uint printf(char[] format, ...) + { + va_list ap; + ap = cast(va_list) &format; + ap += format.size; + return vprintf(format, ap); + } + + // copies all data from given stream into this one, + // may throw ReadError or WriteError on failure + void copyFrom(Stream s) + { + uint pos = position(); + s.position(0); + copyFrom(s, s.size()); + s.position(pos); + } + + // copies specified number of bytes from given stream into + // this one, may throw ReadError or WriteError on failure + void copyFrom(Stream s, uint count) + { + ubyte[] buf; + buf.length = s.size(); + s.readExact(buf, buf.length); + writeExact(buf, buf.length); + } + // moves pointer to given position, relative to beginning of stream, // end of stream, or current position, returns new position - uint seek(uint offset, int rel) - in - { - assert(seekable); - } - body - { - return 0; - } + abstract ulong seek(long offset, SeekPos whence); + + // seek from the beginning of the stream. + ulong seekSet(long offset) { return seek (offset, SeekPos.Set); } + + // seek from the current point in the stream. + ulong seekCur(long offset) { return seek (offset, SeekPos.Current); } + + // seek from the end of the stream. + ulong seekEnd(long offset) { return seek (offset, SeekPos.End); } // sets position - void position(uint pos) { seek(pos, set); } + void position(ulong pos) { seek(pos, SeekPos.Set); } // returns current position - uint position() { return seek(0, current); } + ulong position() { return seek(0, SeekPos.Current); } // returns size of stream - uint size() + ulong size() { - uint pos = position(), result = seek(0, end); + ulong pos = position(), result = seek(0, SeekPos.End); position(pos); return result; } // returns true if end of stream is reached, false otherwise bit eof() { return position() == size(); } + + // creates a string in memory containing copy of stream data + override char[] toString() + { + uint pos = position(); + char[] result; + result.length = size(); + position(0); + readBlock(result, result.length); + position(pos); + return result; + } + + // calculates CRC-32 of data in stream + override uint toHash() + { + ulong pos = position(); + uint crc = init_crc32 (); + position(0); + for (long i = 0; i < size(); i++) + { + ubyte c; + read(c); + crc = update_crc32(c, crc); + } + position(pos); + return crc; + } } // generic File error, base class for all @@ -209,75 +848,99 @@ class CreateError: StreamFileError this(char[] msg) { super(msg); } } +// access modes; may be or'ed +enum FileMode +{ + In = 1, + Out = 2, + // for compatibility with older versions + input = In, + output = Out +} + // just a file on disk class File: Stream { - // access modes; may be or'ed - enum { toread = 1, towrite = 2 } + import windows; + + // for compatibility with old versions... + deprecated enum: FileMode + { + toread = FileMode.In, + towrite = FileMode.Out + } private HANDLE hFile; - this() { hFile = (HANDLE)null; } + this() { hFile = null; } + + // opens existing handle; use with care! + this(HANDLE hFile, FileMode mode) + { + this.hFile = hFile; + readable = cast(bit)(mode & FileMode.In); + writeable = cast(bit)(mode & FileMode.Out); + } // opens file for reading this(char[] filename) { this(); open(filename); } // opens file in requested mode - this(char[] filename, int mode) { this(); open(filename, mode); } + this(char[] filename, FileMode mode) { this(); open(filename, mode); } // destructor, closes file if still opened ~this() { close(); } // opens file for reading - void open(char[] filename) { open(filename, toread); } + void open(char[] filename) { open(filename, FileMode.In); } // opens file in requested mode - void open(char[] filename, int mode) + void open(char[] filename, FileMode mode) { close(); int access = 0, share = 0; - if (mode & toread) + if (mode & FileMode.In) { readable = true; access |= GENERIC_READ; share |= FILE_SHARE_READ; } - if (mode & towrite) + if (mode & FileMode.Out) { writeable = true; access |= GENERIC_WRITE; } seekable = true; - hFile = CreateFileA(toStringz(filename), access, share, null, - OPEN_EXISTING, 0, (HANDLE)null); + hFile = CreateFileA(toStringz(filename), access, share, + null, OPEN_EXISTING, 0, null); if (hFile == INVALID_HANDLE_VALUE) - throw new OpenError("file not found"); + throw new OpenError("file '" ~ filename ~ "' not found"); } // creates file for writing - void create(char[] filename) { create(filename, towrite); } + void create(char[] filename) { create(filename, FileMode.Out); } // creates file in requested mode - void create(char[] filename, int mode) + void create(char[] filename, FileMode mode) { close(); int access = 0, share = 0; - if (mode & toread) + if (mode & FileMode.In) { access |= GENERIC_READ; share |= FILE_SHARE_READ; } - if (mode & towrite) + if (mode & FileMode.Out) { access |= GENERIC_WRITE; } - hFile = CreateFileA(toStringz(filename), access, share, null, - CREATE_ALWAYS, 0, (HANDLE)null); + hFile = CreateFileA(toStringz(filename), access, share, + null, CREATE_ALWAYS, 0, null); seekable = true; - readable = mode & toread; - writeable = mode & towrite; + readable = cast(bit)(mode & FileMode.In); + writeable = cast(bit)(mode & FileMode.Out); if (hFile == INVALID_HANDLE_VALUE) - throw new CreateError("unable to create file"); + throw new CreateError("unable to create file '" ~ filename ~ "'"); } // closes file, if it is open; otherwise, does nothing @@ -286,7 +949,7 @@ class File: Stream if (hFile) { CloseHandle(hFile); - hFile = (HANDLE)null; + hFile = null; readable = writeable = seekable = false; } } @@ -315,7 +978,7 @@ class File: Stream return size; } - override uint seek(uint offset, int rel) + override ulong seek(long offset, SeekPos rel) // since in-blocks are not inherited, redefine them in { @@ -330,19 +993,80 @@ class File: Stream } // OS-specific property, just in case somebody wants - // to mess with WinAPI + // to mess with underlying API HANDLE handle() { return hFile; } + + // run a few tests + unittest + { + File file = new File; + int i = 666; + file.create("stream.$$$"); + // should be ok to write + assert(file.writeable); + file.writeLine("Testing stream.d:"); + file.writeString("Hello, world!"); + file.write(i); + // string#1 + string#2 + int should give exacly that + assert(file.position() == 19 + 13 + 4); + // we must be at the end of file + assert(file.eof()); + file.close(); + // no operations are allowed when file is closed + assert(!file.readable && !file.writeable && !file.seekable); + file.open("stream.$$$"); + // should be ok to read + assert(file.readable); + assert(!string.cmp(file.readLine(), "Testing stream.d:")); + // jump over "Hello, " + file.seek(7, SeekPos.Current); + assert(file.position() == 19 + 7); + assert(!string.cmp(file.readString(6), "world!")); + i = 0; file.read(i); + assert(i == 666); + // string#1 + string#2 + int should give exacly that + assert(file.position() == 19 + 13 + 4); + // we must be at the end of file + assert(file.eof()); + file.close(); + remove("stream.$$$"); + } } // virtual stream residing in memory class MemoryStream: Stream { - import string; + ubyte[] buf; // current data + uint len; // current data length + uint cur; // current file position - private char[] stream; - private uint ptr = 0; + // clear to an empty buffer. + this() { this((ubyte[]) null); } + + // use this buffer, non-copying. + this(ubyte[] buf) + { + super (); + this.buf = buf; + this.len = buf.length; + readable = writeable = seekable = true; + } + + // use this buffer, non-copying. + this(byte[] buf) { this((ubyte[]) buf); } + + // use this buffer, non-copying. + this(char[] buf) { this((ubyte[]) buf); } + + // ensure the stream can hold this many bytes. + void reserve(uint count) + { + if (cur + count > buf.length) + buf.length = (cur + count) * 2; + } - this() { readable = writeable = seekable = true; } + // returns pointer to stream data + ubyte[] data() { return buf [0 .. len]; } override uint readBlock(void* buffer, uint size) // since in-blocks are not inherited, redefine them @@ -352,11 +1076,11 @@ class MemoryStream: Stream } body { - char* cbuf = cast(char*) buffer; - if (stream.length - ptr < size) - size = stream.length - ptr; - cbuf[0 .. size] = stream[ptr .. ptr+size]; - ptr += size; + ubyte* cbuf = cast(ubyte*) buffer; + if (len - cur < size) + size = len - cur; + cbuf[0 .. size] = buf[cur .. cur + size]; + cur += size; return size; } @@ -368,17 +1092,17 @@ class MemoryStream: Stream } body { - char* cbuf = cast(char*) buffer; - uint rewrite = stream.length - ptr; - if (rewrite > size) - rewrite = size; - stream[ptr .. ptr+rewrite] = cbuf[0 .. rewrite]; - stream ~= cbuf[rewrite .. size]; - ptr += size; + ubyte* cbuf = cast(ubyte*) buffer; + + reserve (size); + buf[cur .. cur + size] = cbuf[0 .. size]; + cur += size; + if (cur > len) + len = cur; return size; } - override uint seek(uint offset, int rel) + override ulong seek(long offset, SeekPos rel) // since in-blocks are not inherited, redefine them in { @@ -386,25 +1110,243 @@ class MemoryStream: Stream } body { + long scur; // signed to saturate to 0 properly + switch (rel) { - case set: ptr = offset; break; - case current: ptr += offset; break; - case end: ptr = stream.length - offset; break; + case SeekPos.Set: scur = offset; break; + case SeekPos.Current: scur = cur + offset; break; + case SeekPos.End: scur = len + offset; break; } - if (ptr < 0) - ptr = 0; - else if (ptr > stream.length) - ptr = stream.length; - return ptr; + + if (scur < 0) + cur = 0; + else if (scur > len) + cur = len; + else + cur = scur; + + return cur; } - // returns stream contents in form of string - char[] toString() + override char[] toString() { - return stream; + return (char[]) data (); } + + /* Test the whole class. */ + unittest + { + MemoryStream m; + + m = new MemoryStream (); + m.writeString ("Hello, world"); + assert (m.position () == 12); + assert (m.seekSet (0) == 0); + assert (m.seekCur (4) == 4); + assert (m.seekEnd (-8) == 4); + assert (m.size () == 12); + assert (m.readString (4) == "o, w"); + m.writeString ("ie"); + assert ((char[]) m.data () == "Hello, wield"); + m.seekEnd (0); + m.writeString ("Foo"); + assert (m.position () == 15); + m.writeString ("Foo foo foo foo foo foo foo"); + assert (m.position () == 42); + } } -// whatever... -alias MemoryStream StringStream; +// slices off a portion of another stream, making seeking +// relative to the boundaries of the slice. +class SliceStream : Stream +{ + Stream base; // stream to base this off of. + ulong low; // low stream offset. + ulong high; // high stream offset. + bit bounded; // upper-bounded by high. + + // set the base stream and the low offset but leave the high unbounded. + this (Stream base, ulong low) + in + { + assert (base !== null); + assert (low <= base.size ()); + } + body + { + super (); + this.base = base; + this.low = low; + this.high = 0; + this.bounded = false; + readable = base.readable; + writeable = base.writeable; + seekable = base.seekable; + } + + // set the base stream, the low offset, and the high offset. + this (Stream base, ulong low, ulong high) + in + { + assert (base !== null); + assert (low <= high); + assert (high <= base.size ()); + } + body + { + super (); + this.base = base; + this.low = low; + this.high = high; + this.bounded = true; + readable = base.readable; + writeable = base.writeable; + seekable = base.seekable; + } + + override uint readBlock (void *buffer, uint size) + in + { + assert (readable); + } + body + { + if (bounded) + { + ulong pos = base.position (); + + if (pos > high) + return 0; + if (size > high - pos) + size = high - pos; + } + + return base.readBlock (buffer, size); + } + + override uint writeBlock (void *buffer, uint size) + in + { + assert (writeable); + } + body + { + if (bounded) + { + ulong pos = base.position (); + + if (pos > high) + return 0; + if (size > high - pos) + size = high - pos; + } + + return base.writeBlock (buffer, size); + } + + override ulong seek(long offset, SeekPos rel) + in + { + assert (seekable); + } + body + { + long output; + + switch (rel) + { + case SeekPos.Set: + output = low; + break; + + case SeekPos.Current: + output = base.position () + offset; + break; + + case SeekPos.End: + if (bounded) + output = high + offset; + else + { + output = base.seek (offset, SeekPos.End); + assert (output >= low); + return output - low; + } + } + + if (output < low) + output = low; + else if (bounded && output > high) + output = high; + + output = base.seek (output, SeekPos.Set); + assert (output >= low); + return output - low; + } + + /* Test the whole class. */ + unittest + { + MemoryStream m; + SliceStream s; + + m = new MemoryStream ("Hello, world"); + s = new SliceStream (m, 4, 8); + assert (s.size () == 4); + assert (s.writeBlock ((char *) "Vroom", 5) == 4); + assert (s.position () == 4); + assert (s.seekEnd (-2) == 2); + assert (s.seekEnd (2) == 4); + s = new SliceStream (m, 4); + assert (s.size () == 8); + assert (s.toString () == "Vrooorld"); + s.seekEnd (0); + s.writeString (", etcetera."); + assert (s.position () == 19); + assert (s.seekSet (0) == 0); + assert (m.position () == 4); + } +} + +// helper functions +static bit iswhite(char c) +{ + return c == " " || c == "\t" || c == "\r" || c == "\n"; +} + +static bit isdigit(char c) +{ + return c >= "0" && c <= "9"; +} + +static bit isoctdigit(char c) +{ + return c >= "0" && c <= "7"; +} + +static bit ishexdigit(char c) +{ + return isdigit(c) || (c >= "A" && c <= "F") || (c >= "a" && c <= "f"); +} + +// API imports +private static extern(Windows) +{ + private import windows; + HANDLE GetStdHandle(DWORD); +} + +// standard IO devices +File stdin, stdout, stderr; + +static this() +{ + // open standard I/O devices + stdin = new File(GetStdHandle(-10), FileMode.In); + stdout = new File(GetStdHandle(-11), FileMode.Out); + stderr = new File(GetStdHandle(-12), FileMode.Out); +} + +import string; +import file; diff --git a/string.d b/string.d index e1a49a531..676ea6bfd 100644 --- a/string.d +++ b/string.d @@ -42,6 +42,8 @@ extern (C) char *strrchr(char *, char); char *memchr(char *, char, uint); void *memcpy(void *, void *, uint); + void *memmove(void *, void *, uint); + void *memset(void *, uint, uint); } /************** Exceptions ****************/ @@ -50,7 +52,7 @@ class StringError : Error { this(char[] msg) { - this.msg = msg; + super(msg); } } @@ -180,8 +182,8 @@ char* toStringz(char[] string) char* p; char[] copy; - if (string == null) - return null; + if (string.length == 0) + return ""; p = &string[0] + string.length; @@ -207,6 +209,10 @@ unittest char foo[] = "abbzxyzzy"; p = toStringz(foo[3..5]); assert(strlen(p) == 2); + + char[] test = ""; + p = toStringz(test); + assert(*p == 0); } /****************************************** @@ -437,7 +443,7 @@ unittest s2 = toupper(s1); assert(cmp(s2, "FOL") == 0); - assert(s2 != s1); + assert(s2 !== s1); } @@ -469,7 +475,7 @@ unittest s2 = capitalize(s1); assert(cmp(s2, "FoL") == 0); - assert(s2 != s1); + assert(s2 !== s1); } @@ -1236,9 +1242,9 @@ char[] translate(char[] s, char[] transtab, char[] delchars) int count; bit[256] deltab; - deltab[] = 0; + deltab[] = false; for (i = 0; i < delchars.length; i++) - deltab[delchars[i]] = 1; + deltab[delchars[i]] = true; count = 0; for (i = 0; i < s.length; i++) @@ -1328,3 +1334,30 @@ unittest i = cmp(r, "123"); assert(i == 0); } + +/************************************************* + * Convert to char[]. + */ + +char[] toString(char *s) +{ + return s ? s[0 .. strlen(s)] : cast(char[])null; +} + +unittest +{ + debug(string) printf("string.toString.unittest\n"); + + char[] r; + int i; + + r = toString(null); + i = cmp(r, ""); + assert(i == 0); + + r = toString("foo\0"); + i = cmp(r, "foo"); + assert(i == 0); +} + + diff --git a/switch.d b/switch.d index 86c6daf83..71bb592cf 100644 --- a/switch.d +++ b/switch.d @@ -50,8 +50,10 @@ int _d_switch_string(char[][] table, char[] ca) // Not found for (i = 0; i < table.length; i++) { - c = memcmp(table[i], ca, ca.length); - assert(c != 0); + if (table[i].length == ca.length) + { c = memcmp(table[i], ca, ca.length); + assert(c != 0); + } } } else @@ -60,11 +62,14 @@ int _d_switch_string(char[][] table, char[] ca) for (i = 0; 1; i++) { assert(i < table.length); - c = memcmp(table[i], ca, ca.length); - if (c == 0) + if (table[i].length == ca.length) { - assert(i == result); - break; + c = memcmp(table[i], ca, ca.length); + if (c == 0) + { + assert(i == result); + break; + } } } } @@ -117,3 +122,139 @@ int _d_switch_string(char[][] table, char[] ca) //printf("not found\n"); return -1; // not found } + +unittest +{ + switch ((char []) "c") + { + case "coo": + default: + break; + } +} + +/********************************** + * Same thing, but for wide chars. + */ + +int _d_switch_ustring(wchar[][] table, wchar[] ca) + in + { + //printf("in _d_switch_ustring()\n"); + assert(table.length >= 0); + assert(ca.length >= 0); + + // Make sure table[] is sorted correctly + int i; + + for (i = 1; i < table.length; i++) + { + int len1 = table[i - 1].length; + int len2 = table[i].length; + + assert(len1 <= len2); + if (len1 == len2) + { + int c; + + c = memcmp(table[i - 1], table[i], len1 * wchar.size); + assert(c < 0); // c==0 means a duplicate + } + } + } + out (result) + { + int i; + int c; + + //printf("out _d_switch_string()\n"); + if (result == -1) + { + // Not found + for (i = 0; i < table.length; i++) + { + if (table[i].length == ca.length) + { c = memcmp(table[i], ca, ca.length * wchar.size); + assert(c != 0); + } + } + } + else + { + assert(0 <= result && result < table.length); + for (i = 0; 1; i++) + { + assert(i < table.length); + if (table[i].length == ca.length) + { + c = memcmp(table[i], ca, ca.length * wchar.size); + if (c == 0) + { + assert(i == result); + break; + } + } + } + } + } + body + { + //printf("body _d_switch_ustring()\n"); + int low; + int high; + int mid; + int c; + wchar[] pca; + + low = 0; + high = table.length; + + /* + // Print table + wprintf("ca[] = '%.*s'\n", ca); + for (mid = 0; mid < high; mid++) + { + pca = table[mid]; + wprintf("table[%d] = %d, '%.*s'\n", mid, pca.length, pca); + } + */ + + // Do binary search + while (low < high) + { + mid = (low + high) >> 1; + pca = table[mid]; + c = ca.length - pca.length; + if (c == 0) + { + c = memcmp(ca, pca, ca.length * wchar.size); + if (c == 0) + { //printf("found %d\n", mid); + return mid; + } + } + if (c < 0) + { + high = mid; + } + else + { + low = mid + 1; + } + } + //printf("not found\n"); + return -1; // not found + } + + +unittest +{ + switch ((wchar []) "c") + { + case "coo": + default: + break; + } +} + + diff --git a/switcherr.d b/switcherr.d new file mode 100644 index 000000000..8f9b7a4a9 --- /dev/null +++ b/switcherr.d @@ -0,0 +1,42 @@ + +import object; +import c.stdio; + +class SwitchError : Object +{ + private: + + uint linnum; + char[] filename; + + this(char[] filename, uint linnum) + { + this.linnum = linnum; + this.filename = filename; + } + + public: + + /*************************************** + * If nobody catches the Assert, this winds up + * getting called by the startup code. + */ + + void print() + { + printf("Switch Default %s(%u)\n", (char *)filename, linnum); + } +} + +/******************************************** + * Called by the compiler generated module assert function. + * Builds an Assert exception and throws it. + */ + +extern (C) static void _d_switch_error(char[] filename, uint line) +{ + //printf("_d_switch_error(%s, %d)\n", (char *)filename, line); + SwitchError a = new SwitchError(filename, line); + //printf("assertion %p created\n", a); + throw a; +} diff --git a/system.d b/system.d index 6fca8ec82..4ea7694cd 100644 --- a/system.d +++ b/system.d @@ -42,3 +42,4 @@ uint os_major = 4; uint os_minor = 0; +// processor: i386 diff --git a/thread.d b/thread.d new file mode 100644 index 000000000..622827f94 --- /dev/null +++ b/thread.d @@ -0,0 +1,327 @@ +// Copyright (c) 2002 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com + +//debug=thread; + +import windows; + +extern (Windows) alias uint (*stdfp)(void *); + +extern (C) + thread_hdl _beginthreadex(void* security, uint stack_size, + stdfp start_addr, void* arglist, uint initflag, + thread_id* thrdaddr); + +// This is equivalent to a HANDLE from windows.d +alias HANDLE thread_hdl; + +alias uint thread_id; + +class ThreadError : Error +{ + this(char[] s) + { + super("Thread error: " ~ s); + } +} + +class Thread +{ + this() + { + } + + this(int (*fp)(void *), void *arg) + { + this.fp = fp; + this.arg = arg; + } + + this(int delegate() dg) + { + this.dg = dg; + } + + thread_hdl hdl; + thread_id id; + void* stackBottom; + + void start() + { + if (state != TS.INITIAL) + error("already started"); + + synchronized (threadLock) + { + for (int i = 0; 1; i++) + { + if (i == allThreads.length) + error("too many threads"); + if (!allThreads[i]) + { allThreads[i] = this; + idx = i; + if (i >= allThreadsDim) + allThreadsDim = i + 1; + break; + } + } + nthreads++; + } + + state = TS.RUNNING; + hdl = _beginthreadex(null, 0, &threadstart, this, 0, &id); + if (hdl == cast(thread_hdl)0) + { state = TS.TERMINATED; + allThreads[idx] = null; + idx = -1; + error("failed to start"); + } + } + + int run() + { + if (fp) + return fp(arg); + else if (dg) + return dg(); + } + + void wait() + { + if (this === getThis()) + error("wait on self"); + if (state == TS.RUNNING) + { DWORD dw; + + dw = WaitForSingleObject(hdl, 0xFFFFFFFF); + } + } + + void wait(uint milliseconds) + { + if (this === getThis()) + error("wait on self"); + if (state == TS.RUNNING) + { DWORD dw; + + dw = WaitForSingleObject(hdl, milliseconds); + } + } + + enum TS + { + INITIAL, + RUNNING, + TERMINATED + } + + TS getState() + { + return state; + } + + enum PRIORITY + { + INCREASE, + DECREASE, + IDLE, + CRITICAL + } + + void setPriority(PRIORITY p) + { + int nPriority; + + switch (p) + { + case PRIORITY.INCREASE: + nPriority = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case PRIORITY.DECREASE: + nPriority = THREAD_PRIORITY_BELOW_NORMAL; + break; + case PRIORITY.IDLE: + nPriority = THREAD_PRIORITY_IDLE; + break; + case PRIORITY.CRITICAL: + nPriority = THREAD_PRIORITY_TIME_CRITICAL; + break; + } + + if (SetThreadPriority(hdl, nPriority) == THREAD_PRIORITY_ERROR_RETURN) + error("set priority"); + } + + static Thread getThis() + { + thread_id id; + Thread result; + + //printf("getThis(), allThreadsDim = %d\n", allThreadsDim); + synchronized (threadLock) + { + id = GetCurrentThreadId(); + for (int i = 0; i < allThreadsDim; i++) + { + Thread t = allThreads[i]; + if (t && id == t.id) + { + return t; + } + } + } + printf("didn't find it\n"); + assert(result); + return result; + } + + static Thread[] getAll() + { + return allThreads[0 .. allThreadsDim]; + } + + void pause() + { + if (state != TS.RUNNING || SuspendThread(hdl) == 0xFFFFFFFF) + error("cannot pause"); + } + + void resume() + { + if (state != TS.RUNNING || ResumeThread(hdl) == 0xFFFFFFFF) + error("cannot resume"); + } + + static void pauseAll() + { + if (nthreads > 1) + { + Thread tthis = getThis(); + + for (int i = 0; i < allThreadsDim; i++) + { Thread t; + + t = allThreads[i]; + if (t && t !== tthis && t.state == TS.RUNNING) + t.pause(); + } + } + } + + static void resumeAll() + { + if (nthreads > 1) + { + Thread tthis = getThis(); + + for (int i = 0; i < allThreadsDim; i++) + { Thread t; + + t = allThreads[i]; + if (t && t !== tthis && t.state == TS.RUNNING) + t.resume(); + } + } + } + + static void yield() + { + Sleep(0); + } + + static uint nthreads = 1; + + private: + + static uint allThreadsDim; + static Object threadLock; + static Thread[0x400] allThreads; // length matches value in C runtime + + TS state; + int idx = -1; // index into allThreads[] + + int (*fp)(void *); + void *arg; + + int delegate() dg; + + void error(char[] msg) + { + throw new ThreadError(msg); + } + + + /************************************************ + * This is just a wrapper to interface between C rtl and Thread.run(). + */ + + extern (Windows) static uint threadstart(void *p) + { + Thread t = cast(Thread)p; + int result; + + debug (thread) printf("Starting thread %d\n", t.idx); + t.stackBottom = os_query_stackBottom(); + try + { + result = t.run(); + } + catch (Object o) + { + printf("Error: "); + o.print(); + result = 1; + } + + debug (thread) printf("Ending thread %d\n", t.idx); + t.state = TS.TERMINATED; + allThreads[t.idx] = null; + t.idx = -1; + nthreads--; + return result; + } + + + /************************************** + * Create a Thread for global main(). + */ + + static this() + { + threadLock = new Object(); + + Thread t = new Thread(); + + t.state = TS.RUNNING; + t.id = GetCurrentThreadId(); + t.hdl = GetCurrentThread(); + t.stackBottom = os_query_stackBottom(); + synchronized (threadLock) + { + assert(!allThreads[0]); + allThreads[0] = t; + allThreadsDim = 1; + t.idx = 0; + } + } + +} + + +/********************************************** + * Determine "bottom" of stack (actually the top on Win32 systems). + */ + +void *os_query_stackBottom() +{ + asm + { + naked ; + mov EAX,FS:4 ; + ret ; + } +} + + diff --git a/ti_Aa.d b/ti_Aa.d new file mode 100644 index 000000000..6055568b8 --- /dev/null +++ b/ti_Aa.d @@ -0,0 +1,71 @@ + +import string; + +// char[] + +class TypeInfo_Aa : TypeInfo +{ + uint getHash(void *p) + { char[] s = *(char[]*)p; + uint len = s.length; + char *str = s; + uint hash = 0; + + while (1) + { + switch (len) + { + case 0: + return hash; + + case 1: + hash *= 9; + hash += *(ubyte *)str; + return hash; + + case 2: + hash *= 9; + hash += *(ushort *)str; + return hash; + + case 3: + hash *= 9; + hash += (*(ushort *)str << 8) + + ((ubyte *)str)[2]; + return hash; + + default: + hash *= 9; + hash += *(uint *)str; + str += 4; + len -= 4; + break; + } + } + + return hash; + } + + int equals(void *p1, void *p2) + { + char[] s1 = *(char[]*)p1; + char[] s2 = *(char[]*)p2; + + return s1.length == s2.length && + memcmp((char *)s1, (char *)s2, s1.length) == 0; + } + + int compare(void *p1, void *p2) + { + char[] s1 = *(char[]*)p1; + char[] s2 = *(char[]*)p2; + + return string.cmp(s1, s2); + } + + int tsize() + { + return (char[]).size; + } +} + diff --git a/ti_C.d b/ti_C.d new file mode 100644 index 000000000..cbf74016b --- /dev/null +++ b/ti_C.d @@ -0,0 +1,44 @@ + +import string; + +// Object + +class TypeInfo_C : TypeInfo +{ + uint getHash(void *p) + { + Object o = *(Object*)p; + assert(o); + return o.toHash(); + } + + int equals(void *p1, void *p2) + { + Object o1 = *(Object*)p1; + Object o2 = *(Object*)p2; + + return o1 == o2 || (o1 && o1.cmp(o2) == 0); + } + + int compare(void *p1, void *p2) + { + Object o1 = *(Object*)p1; + Object o2 = *(Object*)p2; + int c = 0; + + if (o1 != o2) + { + if (o1) + c = o1.cmp(o2); + else + c = -o2.cmp(o1); + } + return c; + } + + int tsize() + { + return Object.size; + } +} + diff --git a/ti_byte.d b/ti_byte.d new file mode 100644 index 000000000..554287422 --- /dev/null +++ b/ti_byte.d @@ -0,0 +1,35 @@ + +// byte + +class TypeInfo_g : TypeInfo +{ + uint getHash(void *p) + { + return *(byte *)p; + } + + int equals(void *p1, void *p2) + { + return *(byte *)p1 == *(byte *)p2; + } + + int compare(void *p1, void *p2) + { + return *(byte *)p1 - *(byte *)p2; + } + + int tsize() + { + return byte.size; + } + + void swap(void *p1, void *p2) + { + byte t; + + t = *(byte *)p1; + *(byte *)p1 = *(byte *)p2; + *(byte *)p2 = t; + } +} + diff --git a/ti_char.d b/ti_char.d new file mode 100644 index 000000000..45e91619f --- /dev/null +++ b/ti_char.d @@ -0,0 +1,34 @@ + + +class TypeInfo_a : TypeInfo +{ + uint getHash(void *p) + { + return *(char *)p; + } + + int equals(void *p1, void *p2) + { + return *(char *)p1 == *(char *)p2; + } + + int compare(void *p1, void *p2) + { + return *(char *)p1 - *(char *)p2; + } + + int tsize() + { + return char.size; + } + + void swap(void *p1, void *p2) + { + char t; + + t = *(char *)p1; + *(char *)p1 = *(char *)p2; + *(char *)p2 = t; + } +} + diff --git a/ti_delegate.d b/ti_delegate.d new file mode 100644 index 000000000..3f128fd99 --- /dev/null +++ b/ti_delegate.d @@ -0,0 +1,33 @@ + +// delegate + +alias void delegate(int) dg; + +class TypeInfo_D : TypeInfo +{ + uint getHash(void *p) + { long l = *(long *)p; + + return (uint)(l + (l >> 32)); + } + + int equals(void *p1, void *p2) + { + return *(dg *)p1 == *(dg *)p2; + } + + int tsize() + { + return dg.size; + } + + void swap(void *p1, void *p2) + { + dg t; + + t = *(dg *)p1; + *(dg *)p1 = *(dg *)p2; + *(dg *)p2 = t; + } +} + diff --git a/ti_double.d b/ti_double.d new file mode 100644 index 000000000..a302a6ae5 --- /dev/null +++ b/ti_double.d @@ -0,0 +1,35 @@ + +// double + +class TypeInfo_d : TypeInfo +{ + uint getHash(void *p) + { + return *(double *)p; + } + + int equals(void *p1, void *p2) + { + return *(double *)p1 == *(double *)p2; + } + + int compare(void *p1, void *p2) + { + return *(double *)p1 - *(double *)p2; + } + + int tsize() + { + return double.size; + } + + void swap(void *p1, void *p2) + { + double t; + + t = *(double *)p1; + *(double *)p1 = *(double *)p2; + *(double *)p2 = t; + } +} + diff --git a/ti_extended.d b/ti_extended.d new file mode 100644 index 000000000..02a02a26c --- /dev/null +++ b/ti_extended.d @@ -0,0 +1,35 @@ + +// extended + +class TypeInfo_e : TypeInfo +{ + uint getHash(void *p) + { + return *(extended *)p; + } + + int equals(void *p1, void *p2) + { + return *(extended *)p1 == *(extended *)p2; + } + + int compare(void *p1, void *p2) + { + return *(extended *)p1 - *(extended *)p2; + } + + int tsize() + { + return extended.size; + } + + void swap(void *p1, void *p2) + { + extended t; + + t = *(extended *)p1; + *(extended *)p1 = *(extended *)p2; + *(extended *)p2 = t; + } +} + diff --git a/ti_float.d b/ti_float.d new file mode 100644 index 000000000..bb1b3bf37 --- /dev/null +++ b/ti_float.d @@ -0,0 +1,35 @@ + +// float + +class TypeInfo_f : TypeInfo +{ + uint getHash(void *p) + { + return *(float *)p; + } + + int equals(void *p1, void *p2) + { + return *(float *)p1 == *(float *)p2; + } + + int compare(void *p1, void *p2) + { + return *(float *)p1 - *(float *)p2; + } + + int tsize() + { + return float.size; + } + + void swap(void *p1, void *p2) + { + float t; + + t = *(float *)p1; + *(float *)p1 = *(float *)p2; + *(float *)p2 = t; + } +} + diff --git a/ti_int.d b/ti_int.d new file mode 100644 index 000000000..cb5f70ce4 --- /dev/null +++ b/ti_int.d @@ -0,0 +1,34 @@ + + +class TypeInfo_i : TypeInfo +{ + uint getHash(void *p) + { + return *(uint *)p; + } + + int equals(void *p1, void *p2) + { + return *(uint *)p1 == *(uint *)p2; + } + + int compare(void *p1, void *p2) + { + return *(int *)p1 - *(int *)p2; + } + + int tsize() + { + return int.size; + } + + void swap(void *p1, void *p2) + { + int t; + + t = *(int *)p1; + *(int *)p1 = *(int *)p2; + *(int *)p2 = t; + } +} + diff --git a/ti_long.d b/ti_long.d new file mode 100644 index 000000000..36a46c618 --- /dev/null +++ b/ti_long.d @@ -0,0 +1,35 @@ + +// long + +class TypeInfo_l : TypeInfo +{ + uint getHash(void *p) + { + return *(long *)p; + } + + int equals(void *p1, void *p2) + { + return *(long *)p1 == *(long *)p2; + } + + int compare(void *p1, void *p2) + { + return *(long *)p1 - *(long *)p2; + } + + int tsize() + { + return long.size; + } + + void swap(void *p1, void *p2) + { + long t; + + t = *(long *)p1; + *(long *)p1 = *(long *)p2; + *(long *)p2 = t; + } +} + diff --git a/ti_ptr.d b/ti_ptr.d new file mode 100644 index 000000000..083237e17 --- /dev/null +++ b/ti_ptr.d @@ -0,0 +1,35 @@ + +// pointer + +class TypeInfo_P : TypeInfo +{ + uint getHash(void *p) + { + return (uint)*(void* *)p; + } + + int equals(void *p1, void *p2) + { + return *(void* *)p1 == *(void* *)p2; + } + + int compare(void *p1, void *p2) + { + return *(void* *)p1 - *(void* *)p2; + } + + int tsize() + { + return (void*).size; + } + + void swap(void *p1, void *p2) + { + void* t; + + t = *(void* *)p1; + *(void* *)p1 = *(void* *)p2; + *(void* *)p2 = t; + } +} + diff --git a/ti_short.d b/ti_short.d new file mode 100644 index 000000000..0fd7c2c43 --- /dev/null +++ b/ti_short.d @@ -0,0 +1,35 @@ + +// short + +class TypeInfo_s : TypeInfo +{ + uint getHash(void *p) + { + return *(short *)p; + } + + int equals(void *p1, void *p2) + { + return *(short *)p1 == *(short *)p2; + } + + int compare(void *p1, void *p2) + { + return *(short *)p1 - *(short *)p2; + } + + int tsize() + { + return short.size; + } + + void swap(void *p1, void *p2) + { + short t; + + t = *(short *)p1; + *(short *)p1 = *(short *)p2; + *(short *)p2 = t; + } +} + diff --git a/ti_ubyte.d b/ti_ubyte.d new file mode 100644 index 000000000..5f9ac82b1 --- /dev/null +++ b/ti_ubyte.d @@ -0,0 +1,35 @@ + +// ubyte + +class TypeInfo_h : TypeInfo +{ + uint getHash(void *p) + { + return *(ubyte *)p; + } + + int equals(void *p1, void *p2) + { + return *(ubyte *)p1 == *(ubyte *)p2; + } + + int compare(void *p1, void *p2) + { + return *(ubyte *)p1 - *(ubyte *)p2; + } + + int tsize() + { + return ubyte.size; + } + + void swap(void *p1, void *p2) + { + ubyte t; + + t = *(ubyte *)p1; + *(ubyte *)p1 = *(ubyte *)p2; + *(ubyte *)p2 = t; + } +} + diff --git a/ti_uint.d b/ti_uint.d new file mode 100644 index 000000000..d072a26a4 --- /dev/null +++ b/ti_uint.d @@ -0,0 +1,34 @@ + + +class TypeInfo_k : TypeInfo +{ + uint getHash(void *p) + { + return *(uint *)p; + } + + int equals(void *p1, void *p2) + { + return *(uint *)p1 == *(uint *)p2; + } + + int compare(void *p1, void *p2) + { + return *(uint *)p1 - *(uint *)p2; + } + + int tsize() + { + return uint.size; + } + + void swap(void *p1, void *p2) + { + int t; + + t = *(uint *)p1; + *(uint *)p1 = *(uint *)p2; + *(uint *)p2 = t; + } +} + diff --git a/ti_ulong.d b/ti_ulong.d new file mode 100644 index 000000000..a66f7e246 --- /dev/null +++ b/ti_ulong.d @@ -0,0 +1,35 @@ + +// ulong + +class TypeInfo_m : TypeInfo +{ + uint getHash(void *p) + { + return *(ulong *)p; + } + + int equals(void *p1, void *p2) + { + return *(ulong *)p1 == *(ulong *)p2; + } + + int compare(void *p1, void *p2) + { + return *(ulong *)p1 - *(ulong *)p2; + } + + int tsize() + { + return ulong.size; + } + + void swap(void *p1, void *p2) + { + ulong t; + + t = *(ulong *)p1; + *(ulong *)p1 = *(ulong *)p2; + *(ulong *)p2 = t; + } +} + diff --git a/ti_ushort.d b/ti_ushort.d new file mode 100644 index 000000000..9e86a01a6 --- /dev/null +++ b/ti_ushort.d @@ -0,0 +1,35 @@ + +// ushort + +class TypeInfo_t : TypeInfo +{ + uint getHash(void *p) + { + return *(ushort *)p; + } + + int equals(void *p1, void *p2) + { + return *(ushort *)p1 == *(ushort *)p2; + } + + int compare(void *p1, void *p2) + { + return *(ushort *)p1 - *(ushort *)p2; + } + + int tsize() + { + return ushort.size; + } + + void swap(void *p1, void *p2) + { + ushort t; + + t = *(ushort *)p1; + *(ushort *)p1 = *(ushort *)p2; + *(ushort *)p2 = t; + } +} + diff --git a/ti_wchar.d b/ti_wchar.d new file mode 100644 index 000000000..50bca8b33 --- /dev/null +++ b/ti_wchar.d @@ -0,0 +1,34 @@ + + +class TypeInfo_u : TypeInfo +{ + uint getHash(void *p) + { + return *(wchar *)p; + } + + int equals(void *p1, void *p2) + { + return *(wchar *)p1 == *(wchar *)p2; + } + + int compare(void *p1, void *p2) + { + return *(wchar *)p1 - *(wchar *)p2; + } + + wchar tsize() + { + return wchar.size; + } + + void swap(void *p1, void *p2) + { + wchar t; + + t = *(wchar *)p1; + *(wchar *)p1 = *(wchar *)p2; + *(wchar *)p2 = t; + } +} + diff --git a/unittest.d b/unittest.d index 7027f8540..83c0ee0c1 100644 --- a/unittest.d +++ b/unittest.d @@ -1,4 +1,9 @@ +// Copyright (c) 1999-2002 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// www.digitalmars.com + // This test program pulls in all the library modules in order // to run the unit tests on them. // Then, it prints out the arguments passed to main(). @@ -8,10 +13,14 @@ import c.stdio; import string; import path; import math; +import math2; import outbuffer; import ctype; import regexp; import random; +import date; +import dateparse; +import stream; int main(char[][] args) { @@ -21,10 +30,16 @@ int main(char[][] args) cmp("foo", "bar"); // string fncharmatch('a', 'b'); // path isnan(1.0); // math + feq(1.0, 2.0); // math2 OutBuffer b = new OutBuffer(); // outbuffer ctype.tolower('A'); // ctype RegExp r = new RegExp(null, null); // regexp rand(); + int a[]; + a.reverse; // adi + a.sort; // qsort + date.getUTCtime(); // date + StreamError se = new StreamError(""); // stream printf("hello world\n"); printf("args.length = %d\n", args.length); diff --git a/windows.d b/windows.d index 0fc7ecac3..9388d9a03 100644 --- a/windows.d +++ b/windows.d @@ -22,6 +22,8 @@ extern (Windows) alias LPSTR PTSTR, LPTSTR; alias LPCSTR LPCTSTR; + alias WCHAR* LPCWSTR, PCWSTR; + alias uint DWORD; alias int BOOL; alias ubyte BYTE; @@ -54,6 +56,30 @@ extern (Windows) alias HANDLE HINSTANCE; alias HINSTANCE HMODULE; alias HANDLE HWND; + + alias HANDLE HGDIOBJ; + alias HANDLE HACCEL; + alias HANDLE HBITMAP; + alias HANDLE HBRUSH; + alias HANDLE HCOLORSPACE; + alias HANDLE HDC; + alias HANDLE HGLRC; + alias HANDLE HDESK; + alias HANDLE HENHMETAFILE; + alias HANDLE HFONT; + alias HANDLE HICON; + alias HANDLE HMENU; + alias HANDLE HMETAFILE; + alias HANDLE HPALETTE; + alias HANDLE HPEN; + alias HANDLE HRGN; + alias HANDLE HRSRC; + alias HANDLE HSTR; + alias HANDLE HTASK; + alias HANDLE HWINSTA; + alias HANDLE HKL; + alias HICON HCURSOR; + alias HANDLE HKEY; alias HKEY *PHKEY; alias DWORD ACCESS_MASK; @@ -62,13 +88,26 @@ extern (Windows) alias int (*FARPROC)(); - DWORD GetTickCount(); + alias UINT WPARAM; + alias LONG LPARAM; + alias LONG LRESULT; + + alias DWORD COLORREF; + alias DWORD *LPCOLORREF; + alias WORD ATOM; + WORD HIWORD(int l) { return (WORD)((l >> 16) & 0xFFFF); } WORD LOWORD(int l) { return (WORD)l; } int FAILED(int status) { return status < 0; } int SUCCEEDED(int Status) { return Status >= 0; } +enum : int +{ + FALSE = 0, + TRUE = 1, +} + enum : uint { MAX_PATH = 260, @@ -78,6 +117,13 @@ enum : uint enum { ERROR_SUCCESS = 0, + ERROR_INVALID_FUNCTION = 1, + ERROR_FILE_NOT_FOUND = 2, + ERROR_PATH_NOT_FOUND = 3, + ERROR_TOO_MANY_OPEN_FILES = 4, + ERROR_ACCESS_DENIED = 5, + ERROR_INVALID_HANDLE = 6, + ERROR_NO_MORE_FILES = 18, } enum @@ -95,7 +141,7 @@ enum FILE_END = 2, } -enum +enum : uint { DELETE = 0x00010000, READ_CONTROL = 0x00020000, @@ -156,7 +202,7 @@ enum const DWORD MAILSLOT_NO_MESSAGE = (DWORD)-1; const DWORD MAILSLOT_WAIT_FOREVER = (DWORD)-1; -enum +enum : uint { FILE_FLAG_WRITE_THROUGH = 0x80000000, FILE_FLAG_OVERLAPPED = 0x40000000, @@ -387,4 +433,1271 @@ export LONG RegCreateKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD Reserved, LPSTR lp export LONG RegSetValueExA(HKEY hKey, LPCSTR lpValueName, DWORD Reserved, DWORD dwType, BYTE* lpData, DWORD cbData); +struct MEMORY_BASIC_INFORMATION { + PVOID BaseAddress; + PVOID AllocationBase; + DWORD AllocationProtect; + DWORD RegionSize; + DWORD State; + DWORD Protect; + DWORD Type; +} +alias MEMORY_BASIC_INFORMATION* PMEMORY_BASIC_INFORMATION; + +enum +{ + SECTION_QUERY = 0x0001, + SECTION_MAP_WRITE = 0x0002, + SECTION_MAP_READ = 0x0004, + SECTION_MAP_EXECUTE = 0x0008, + SECTION_EXTEND_SIZE = 0x0010, + + SECTION_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED|SECTION_QUERY| SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_EXTEND_SIZE), + PAGE_NOACCESS = 0x01, + PAGE_READONLY = 0x02, + PAGE_READWRITE = 0x04, + PAGE_WRITECOPY = 0x08, + PAGE_EXECUTE = 0x10, + PAGE_EXECUTE_READ = 0x20, + PAGE_EXECUTE_READWRITE = 0x40, + PAGE_EXECUTE_WRITECOPY = 0x80, + PAGE_GUARD = 0x100, + PAGE_NOCACHE = 0x200, + MEM_COMMIT = 0x1000, + MEM_RESERVE = 0x2000, + MEM_DECOMMIT = 0x4000, + MEM_RELEASE = 0x8000, + MEM_FREE = 0x10000, + MEM_PRIVATE = 0x20000, + MEM_MAPPED = 0x40000, + MEM_RESET = 0x80000, + MEM_TOP_DOWN = 0x100000, + SEC_FILE = 0x800000, + SEC_IMAGE = 0x1000000, + SEC_RESERVE = 0x4000000, + SEC_COMMIT = 0x8000000, + SEC_NOCACHE = 0x10000000, + MEM_IMAGE = SEC_IMAGE, +} + +// +// Define access rights to files and directories +// + +// +// The FILE_READ_DATA and FILE_WRITE_DATA constants are also defined in +// devioctl.h as FILE_READ_ACCESS and FILE_WRITE_ACCESS. The values for these +// constants *MUST* always be in sync. +// The values are redefined in devioctl.h because they must be available to +// both DOS and NT. +// + +enum +{ + FILE_READ_DATA = ( 0x0001 ), // file & pipe + FILE_LIST_DIRECTORY = ( 0x0001 ), // directory + + FILE_WRITE_DATA = ( 0x0002 ), // file & pipe + FILE_ADD_FILE = ( 0x0002 ), // directory + + FILE_APPEND_DATA = ( 0x0004 ), // file + FILE_ADD_SUBDIRECTORY = ( 0x0004 ), // directory + FILE_CREATE_PIPE_INSTANCE = ( 0x0004 ), // named pipe + + FILE_READ_EA = ( 0x0008 ), // file & directory + + FILE_WRITE_EA = ( 0x0010 ), // file & directory + + FILE_EXECUTE = ( 0x0020 ), // file + FILE_TRAVERSE = ( 0x0020 ), // directory + + FILE_DELETE_CHILD = ( 0x0040 ), // directory + + FILE_READ_ATTRIBUTES = ( 0x0080 ), // all + + FILE_WRITE_ATTRIBUTES = ( 0x0100 ), // all + + FILE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF), + + FILE_GENERIC_READ = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE), + + + FILE_GENERIC_WRITE = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE), + + + FILE_GENERIC_EXECUTE = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE), +} + +export +{ + LPVOID VirtualAlloc(LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, DWORD flProtect); + BOOL VirtualFree(LPVOID lpAddress, DWORD dwSize, DWORD dwFreeType); + BOOL VirtualProtect(LPVOID lpAddress, DWORD dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); + DWORD VirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, DWORD dwLength); + LPVOID VirtualAllocEx(HANDLE hProcess, LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, DWORD flProtect); + BOOL VirtualFreeEx(HANDLE hProcess, LPVOID lpAddress, DWORD dwSize, DWORD dwFreeType); + BOOL VirtualProtectEx(HANDLE hProcess, LPVOID lpAddress, DWORD dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); + DWORD VirtualQueryEx(HANDLE hProcess, LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, DWORD dwLength); +} + +struct SYSTEMTIME +{ + WORD wYear; + WORD wMonth; + WORD wDayOfWeek; + WORD wDay; + WORD wHour; + WORD wMinute; + WORD wSecond; + WORD wMilliseconds; +} + +struct TIME_ZONE_INFORMATION { + LONG Bias; + WCHAR StandardName[ 32 ]; + SYSTEMTIME StandardDate; + LONG StandardBias; + WCHAR DaylightName[ 32 ]; + SYSTEMTIME DaylightDate; + LONG DaylightBias; +} + +enum +{ + TIME_ZONE_ID_UNKNOWN = 0, + TIME_ZONE_ID_STANDARD = 1, + TIME_ZONE_ID_DAYLIGHT = 2, +} + +export void GetSystemTime(SYSTEMTIME* lpSystemTime); +export void GetSystemTimeAsFileTime(FILETIME* lpSystemTimeAsFileTime); +export BOOL SetSystemTime(SYSTEMTIME* lpSystemTime); +export void GetLocalTime(SYSTEMTIME* lpSystemTime); +export BOOL SetLocalTime(SYSTEMTIME* lpSystemTime); +export BOOL SystemTimeToTzSpecificLocalTime(TIME_ZONE_INFORMATION* lpTimeZoneInformation, SYSTEMTIME* lpUniversalTime, SYSTEMTIME* lpLocalTime); +export DWORD GetTimeZoneInformation(TIME_ZONE_INFORMATION* lpTimeZoneInformation); +export BOOL SetTimeZoneInformation(TIME_ZONE_INFORMATION* lpTimeZoneInformation); + +export BOOL SystemTimeToFileTime(SYSTEMTIME *lpSystemTime, FILETIME* lpFileTime); +export BOOL FileTimeToLocalFileTime(FILETIME *lpFileTime, FILETIME* lpLocalFileTime); +export BOOL LocalFileTimeToFileTime(FILETIME *lpLocalFileTime, FILETIME* lpFileTime); +export BOOL FileTimeToSystemTime(FILETIME *lpFileTime, SYSTEMTIME* lpSystemTime); +export LONG CompareFileTime(FILETIME *lpFileTime1, FILETIME *lpFileTime2); +export BOOL FileTimeToDosDateTime(FILETIME *lpFileTime, WORD* lpFatDate, WORD* lpFatTime); +export BOOL DosDateTimeToFileTime(WORD wFatDate, WORD wFatTime, FILETIME* lpFileTime); +export DWORD GetTickCount(); +export BOOL SetSystemTimeAdjustment(DWORD dwTimeAdjustment, BOOL bTimeAdjustmentDisabled); +export BOOL GetSystemTimeAdjustment(DWORD* lpTimeAdjustment, DWORD* lpTimeIncrement, BOOL* lpTimeAdjustmentDisabled); + +struct FLOATING_SAVE_AREA { + DWORD ControlWord; + DWORD StatusWord; + DWORD TagWord; + DWORD ErrorOffset; + DWORD ErrorSelector; + DWORD DataOffset; + DWORD DataSelector; + BYTE RegisterArea[80 ]; + DWORD Cr0NpxState; +} + +enum +{ + SIZE_OF_80387_REGISTERS = 80, +// +// The following flags control the contents of the CONTEXT structure. +// + CONTEXT_i386 = 0x00010000, // this assumes that i386 and + CONTEXT_i486 = 0x00010000, // i486 have identical context records + + CONTEXT_CONTROL = (CONTEXT_i386 | 0x00000001), // SS:SP, CS:IP, FLAGS, BP + CONTEXT_INTEGER = (CONTEXT_i386 | 0x00000002), // AX, BX, CX, DX, SI, DI + CONTEXT_SEGMENTS = (CONTEXT_i386 | 0x00000004), // DS, ES, FS, GS + CONTEXT_FLOATING_POINT = (CONTEXT_i386 | 0x00000008), // 387 state + CONTEXT_DEBUG_REGISTERS = (CONTEXT_i386 | 0x00000010), // DB 0-3,6,7 + + CONTEXT_FULL = (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS), +} + +struct CONTEXT +{ + + // + // The flags values within this flag control the contents of + // a CONTEXT record. + // + // If the context record is used as an input parameter, then + // for each portion of the context record controlled by a flag + // whose value is set, it is assumed that that portion of the + // context record contains valid context. If the context record + // is being used to modify a threads context, then only that + // portion of the threads context will be modified. + // + // If the context record is used as an IN OUT parameter to capture + // the context of a thread, then only those portions of the thread's + // context corresponding to set flags will be returned. + // + // The context record is never used as an OUT only parameter. + // + + DWORD ContextFlags; + + // + // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is + // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT + // included in CONTEXT_FULL. + // + + DWORD Dr0; + DWORD Dr1; + DWORD Dr2; + DWORD Dr3; + DWORD Dr6; + DWORD Dr7; + + // + // This section is specified/returned if the + // ContextFlags word contians the flag CONTEXT_FLOATING_POINT. + // + + FLOATING_SAVE_AREA FloatSave; + + // + // This section is specified/returned if the + // ContextFlags word contians the flag CONTEXT_SEGMENTS. + // + + DWORD SegGs; + DWORD SegFs; + DWORD SegEs; + DWORD SegDs; + + // + // This section is specified/returned if the + // ContextFlags word contians the flag CONTEXT_INTEGER. + // + + DWORD Edi; + DWORD Esi; + DWORD Ebx; + DWORD Edx; + DWORD Ecx; + DWORD Eax; + + // + // This section is specified/returned if the + // ContextFlags word contians the flag CONTEXT_CONTROL. + // + + DWORD Ebp; + DWORD Eip; + DWORD SegCs; // MUST BE SANITIZED + DWORD EFlags; // MUST BE SANITIZED + DWORD Esp; + DWORD SegSs; +} + +enum +{ + THREAD_BASE_PRIORITY_LOWRT = 15, // value that gets a thread to LowRealtime-1 + THREAD_BASE_PRIORITY_MAX = 2, // maximum thread base priority boost + THREAD_BASE_PRIORITY_MIN = -2, // minimum thread base priority boost + THREAD_BASE_PRIORITY_IDLE = -15, // value that gets a thread to idle + + THREAD_PRIORITY_LOWEST = THREAD_BASE_PRIORITY_MIN, + THREAD_PRIORITY_BELOW_NORMAL = (THREAD_PRIORITY_LOWEST+1), + THREAD_PRIORITY_NORMAL = 0, + THREAD_PRIORITY_HIGHEST = THREAD_BASE_PRIORITY_MAX, + THREAD_PRIORITY_ABOVE_NORMAL = (THREAD_PRIORITY_HIGHEST-1), + THREAD_PRIORITY_ERROR_RETURN = int.max, + + THREAD_PRIORITY_TIME_CRITICAL = THREAD_BASE_PRIORITY_LOWRT, + THREAD_PRIORITY_IDLE = THREAD_BASE_PRIORITY_IDLE, +} + +export HANDLE GetCurrentThread(); +export DWORD GetCurrentThreadId(); +export BOOL SetThreadPriority(HANDLE hThread, int nPriority); +export BOOL SetThreadPriorityBoost(HANDLE hThread, BOOL bDisablePriorityBoost); +export BOOL GetThreadPriorityBoost(HANDLE hThread, PBOOL pDisablePriorityBoost); +export int GetThreadPriority(HANDLE hThread); +export BOOL GetThreadContext(HANDLE hThread, CONTEXT* lpContext); +export BOOL SetThreadContext(HANDLE hThread, CONTEXT* lpContext); +export DWORD SuspendThread(HANDLE hThread); +export DWORD ResumeThread(HANDLE hThread); +export DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); +export DWORD WaitForMultipleObjects(DWORD nCount, HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds); +export void Sleep(DWORD dwMilliseconds); + +enum +{ + WM_NOTIFY = 0x004E, + WM_INPUTLANGCHANGEREQUEST = 0x0050, + WM_INPUTLANGCHANGE = 0x0051, + WM_TCARD = 0x0052, + WM_HELP = 0x0053, + WM_USERCHANGED = 0x0054, + WM_NOTIFYFORMAT = 0x0055, + + NFR_ANSI = 1, + NFR_UNICODE = 2, + NF_QUERY = 3, + NF_REQUERY = 4, + + WM_CONTEXTMENU = 0x007B, + WM_STYLECHANGING = 0x007C, + WM_STYLECHANGED = 0x007D, + WM_DISPLAYCHANGE = 0x007E, + WM_GETICON = 0x007F, + WM_SETICON = 0x0080, + + + + WM_NCCREATE = 0x0081, + WM_NCDESTROY = 0x0082, + WM_NCCALCSIZE = 0x0083, + WM_NCHITTEST = 0x0084, + WM_NCPAINT = 0x0085, + WM_NCACTIVATE = 0x0086, + WM_GETDLGCODE = 0x0087, + + WM_NCMOUSEMOVE = 0x00A0, + WM_NCLBUTTONDOWN = 0x00A1, + WM_NCLBUTTONUP = 0x00A2, + WM_NCLBUTTONDBLCLK = 0x00A3, + WM_NCRBUTTONDOWN = 0x00A4, + WM_NCRBUTTONUP = 0x00A5, + WM_NCRBUTTONDBLCLK = 0x00A6, + WM_NCMBUTTONDOWN = 0x00A7, + WM_NCMBUTTONUP = 0x00A8, + WM_NCMBUTTONDBLCLK = 0x00A9, + + WM_KEYFIRST = 0x0100, + WM_KEYDOWN = 0x0100, + WM_KEYUP = 0x0101, + WM_CHAR = 0x0102, + WM_DEADCHAR = 0x0103, + WM_SYSKEYDOWN = 0x0104, + WM_SYSKEYUP = 0x0105, + WM_SYSCHAR = 0x0106, + WM_SYSDEADCHAR = 0x0107, + WM_KEYLAST = 0x0108, + + + WM_IME_STARTCOMPOSITION = 0x010D, + WM_IME_ENDCOMPOSITION = 0x010E, + WM_IME_COMPOSITION = 0x010F, + WM_IME_KEYLAST = 0x010F, + + + WM_INITDIALOG = 0x0110, + WM_COMMAND = 0x0111, + WM_SYSCOMMAND = 0x0112, + WM_TIMER = 0x0113, + WM_HSCROLL = 0x0114, + WM_VSCROLL = 0x0115, + WM_INITMENU = 0x0116, + WM_INITMENUPOPUP = 0x0117, + WM_MENUSELECT = 0x011F, + WM_MENUCHAR = 0x0120, + WM_ENTERIDLE = 0x0121, + + WM_CTLCOLORMSGBOX = 0x0132, + WM_CTLCOLOREDIT = 0x0133, + WM_CTLCOLORLISTBOX = 0x0134, + WM_CTLCOLORBTN = 0x0135, + WM_CTLCOLORDLG = 0x0136, + WM_CTLCOLORSCROLLBAR = 0x0137, + WM_CTLCOLORSTATIC = 0x0138, + + + + WM_MOUSEFIRST = 0x0200, + WM_MOUSEMOVE = 0x0200, + WM_LBUTTONDOWN = 0x0201, + WM_LBUTTONUP = 0x0202, + WM_LBUTTONDBLCLK = 0x0203, + WM_RBUTTONDOWN = 0x0204, + WM_RBUTTONUP = 0x0205, + WM_RBUTTONDBLCLK = 0x0206, + WM_MBUTTONDOWN = 0x0207, + WM_MBUTTONUP = 0x0208, + WM_MBUTTONDBLCLK = 0x0209, + + + + WM_MOUSELAST = 0x0209, + + + + + + + + + WM_PARENTNOTIFY = 0x0210, + MENULOOP_WINDOW = 0, + MENULOOP_POPUP = 1, + WM_ENTERMENULOOP = 0x0211, + WM_EXITMENULOOP = 0x0212, + + + WM_NEXTMENU = 0x0213, +} + +enum +{ +/* + * Dialog Box Command IDs + */ + IDOK = 1, + IDCANCEL = 2, + IDABORT = 3, + IDRETRY = 4, + IDIGNORE = 5, + IDYES = 6, + IDNO = 7, + + IDCLOSE = 8, + IDHELP = 9, + + +// end_r_winuser + + + +/* + * Control Manager Structures and Definitions + */ + + + +// begin_r_winuser + +/* + * Edit Control Styles + */ + ES_LEFT = 0x0000, + ES_CENTER = 0x0001, + ES_RIGHT = 0x0002, + ES_MULTILINE = 0x0004, + ES_UPPERCASE = 0x0008, + ES_LOWERCASE = 0x0010, + ES_PASSWORD = 0x0020, + ES_AUTOVSCROLL = 0x0040, + ES_AUTOHSCROLL = 0x0080, + ES_NOHIDESEL = 0x0100, + ES_OEMCONVERT = 0x0400, + ES_READONLY = 0x0800, + ES_WANTRETURN = 0x1000, + + ES_NUMBER = 0x2000, + + +// end_r_winuser + + + +/* + * Edit Control Notification Codes + */ + EN_SETFOCUS = 0x0100, + EN_KILLFOCUS = 0x0200, + EN_CHANGE = 0x0300, + EN_UPDATE = 0x0400, + EN_ERRSPACE = 0x0500, + EN_MAXTEXT = 0x0501, + EN_HSCROLL = 0x0601, + EN_VSCROLL = 0x0602, + + +/* Edit control EM_SETMARGIN parameters */ + EC_LEFTMARGIN = 0x0001, + EC_RIGHTMARGIN = 0x0002, + EC_USEFONTINFO = 0xffff, + + + + +// begin_r_winuser + +/* + * Edit Control Messages + */ + EM_GETSEL = 0x00B0, + EM_SETSEL = 0x00B1, + EM_GETRECT = 0x00B2, + EM_SETRECT = 0x00B3, + EM_SETRECTNP = 0x00B4, + EM_SCROLL = 0x00B5, + EM_LINESCROLL = 0x00B6, + EM_SCROLLCARET = 0x00B7, + EM_GETMODIFY = 0x00B8, + EM_SETMODIFY = 0x00B9, + EM_GETLINECOUNT = 0x00BA, + EM_LINEINDEX = 0x00BB, + EM_SETHANDLE = 0x00BC, + EM_GETHANDLE = 0x00BD, + EM_GETTHUMB = 0x00BE, + EM_LINELENGTH = 0x00C1, + EM_REPLACESEL = 0x00C2, + EM_GETLINE = 0x00C4, + EM_LIMITTEXT = 0x00C5, + EM_CANUNDO = 0x00C6, + EM_UNDO = 0x00C7, + EM_FMTLINES = 0x00C8, + EM_LINEFROMCHAR = 0x00C9, + EM_SETTABSTOPS = 0x00CB, + EM_SETPASSWORDCHAR = 0x00CC, + EM_EMPTYUNDOBUFFER = 0x00CD, + EM_GETFIRSTVISIBLELINE = 0x00CE, + EM_SETREADONLY = 0x00CF, + EM_SETWORDBREAKPROC = 0x00D0, + EM_GETWORDBREAKPROC = 0x00D1, + EM_GETPASSWORDCHAR = 0x00D2, + + EM_SETMARGINS = 0x00D3, + EM_GETMARGINS = 0x00D4, + EM_SETLIMITTEXT = EM_LIMITTEXT, /* ;win40 Name change */ + EM_GETLIMITTEXT = 0x00D5, + EM_POSFROMCHAR = 0x00D6, + EM_CHARFROMPOS = 0x00D7, + + + +// end_r_winuser + + +/* + * EDITWORDBREAKPROC code values + */ + WB_LEFT = 0, + WB_RIGHT = 1, + WB_ISDELIMITER = 2, + +// begin_r_winuser + +/* + * Button Control Styles + */ + BS_PUSHBUTTON = 0x00000000, + BS_DEFPUSHBUTTON = 0x00000001, + BS_CHECKBOX = 0x00000002, + BS_AUTOCHECKBOX = 0x00000003, + BS_RADIOBUTTON = 0x00000004, + BS_3STATE = 0x00000005, + BS_AUTO3STATE = 0x00000006, + BS_GROUPBOX = 0x00000007, + BS_USERBUTTON = 0x00000008, + BS_AUTORADIOBUTTON = 0x00000009, + BS_OWNERDRAW = 0x0000000B, + BS_LEFTTEXT = 0x00000020, + + BS_TEXT = 0x00000000, + BS_ICON = 0x00000040, + BS_BITMAP = 0x00000080, + BS_LEFT = 0x00000100, + BS_RIGHT = 0x00000200, + BS_CENTER = 0x00000300, + BS_TOP = 0x00000400, + BS_BOTTOM = 0x00000800, + BS_VCENTER = 0x00000C00, + BS_PUSHLIKE = 0x00001000, + BS_MULTILINE = 0x00002000, + BS_NOTIFY = 0x00004000, + BS_FLAT = 0x00008000, + BS_RIGHTBUTTON = BS_LEFTTEXT, + + + +/* + * User Button Notification Codes + */ + BN_CLICKED = 0, + BN_PAINT = 1, + BN_HILITE = 2, + BN_UNHILITE = 3, + BN_DISABLE = 4, + BN_DOUBLECLICKED = 5, + + BN_PUSHED = BN_HILITE, + BN_UNPUSHED = BN_UNHILITE, + BN_DBLCLK = BN_DOUBLECLICKED, + BN_SETFOCUS = 6, + BN_KILLFOCUS = 7, + +/* + * Button Control Messages + */ + BM_GETCHECK = 0x00F0, + BM_SETCHECK = 0x00F1, + BM_GETSTATE = 0x00F2, + BM_SETSTATE = 0x00F3, + BM_SETSTYLE = 0x00F4, + + BM_CLICK = 0x00F5, + BM_GETIMAGE = 0x00F6, + BM_SETIMAGE = 0x00F7, + + BST_UNCHECKED = 0x0000, + BST_CHECKED = 0x0001, + BST_INDETERMINATE = 0x0002, + BST_PUSHED = 0x0004, + BST_FOCUS = 0x0008, + + +/* + * Static Control Constants + */ + SS_LEFT = 0x00000000, + SS_CENTER = 0x00000001, + SS_RIGHT = 0x00000002, + SS_ICON = 0x00000003, + SS_BLACKRECT = 0x00000004, + SS_GRAYRECT = 0x00000005, + SS_WHITERECT = 0x00000006, + SS_BLACKFRAME = 0x00000007, + SS_GRAYFRAME = 0x00000008, + SS_WHITEFRAME = 0x00000009, + SS_USERITEM = 0x0000000A, + SS_SIMPLE = 0x0000000B, + SS_LEFTNOWORDWRAP = 0x0000000C, + + SS_OWNERDRAW = 0x0000000D, + SS_BITMAP = 0x0000000E, + SS_ENHMETAFILE = 0x0000000F, + SS_ETCHEDHORZ = 0x00000010, + SS_ETCHEDVERT = 0x00000011, + SS_ETCHEDFRAME = 0x00000012, + SS_TYPEMASK = 0x0000001F, + + SS_NOPREFIX = 0x00000080, /* Don't do "&" character translation */ + + SS_NOTIFY = 0x00000100, + SS_CENTERIMAGE = 0x00000200, + SS_RIGHTJUST = 0x00000400, + SS_REALSIZEIMAGE = 0x00000800, + SS_SUNKEN = 0x00001000, + SS_ENDELLIPSIS = 0x00004000, + SS_PATHELLIPSIS = 0x00008000, + SS_WORDELLIPSIS = 0x0000C000, + SS_ELLIPSISMASK = 0x0000C000, + + +// end_r_winuser + + +/* + * Static Control Mesages + */ + STM_SETICON = 0x0170, + STM_GETICON = 0x0171, + + STM_SETIMAGE = 0x0172, + STM_GETIMAGE = 0x0173, + STN_CLICKED = 0, + STN_DBLCLK = 1, + STN_ENABLE = 2, + STN_DISABLE = 3, + + STM_MSGMAX = 0x0174, +} + + +enum +{ +/* + * Window Messages + */ + + WM_NULL = 0x0000, + WM_CREATE = 0x0001, + WM_DESTROY = 0x0002, + WM_MOVE = 0x0003, + WM_SIZE = 0x0005, + + WM_ACTIVATE = 0x0006, +/* + * WM_ACTIVATE state values + */ + WA_INACTIVE = 0, + WA_ACTIVE = 1, + WA_CLICKACTIVE = 2, + + WM_SETFOCUS = 0x0007, + WM_KILLFOCUS = 0x0008, + WM_ENABLE = 0x000A, + WM_SETREDRAW = 0x000B, + WM_SETTEXT = 0x000C, + WM_GETTEXT = 0x000D, + WM_GETTEXTLENGTH = 0x000E, + WM_PAINT = 0x000F, + WM_CLOSE = 0x0010, + WM_QUERYENDSESSION = 0x0011, + WM_QUIT = 0x0012, + WM_QUERYOPEN = 0x0013, + WM_ERASEBKGND = 0x0014, + WM_SYSCOLORCHANGE = 0x0015, + WM_ENDSESSION = 0x0016, + WM_SHOWWINDOW = 0x0018, + WM_WININICHANGE = 0x001A, + + WM_SETTINGCHANGE = WM_WININICHANGE, + + + + WM_DEVMODECHANGE = 0x001B, + WM_ACTIVATEAPP = 0x001C, + WM_FONTCHANGE = 0x001D, + WM_TIMECHANGE = 0x001E, + WM_CANCELMODE = 0x001F, + WM_SETCURSOR = 0x0020, + WM_MOUSEACTIVATE = 0x0021, + WM_CHILDACTIVATE = 0x0022, + WM_QUEUESYNC = 0x0023, + + WM_GETMINMAXINFO = 0x0024, +} + +struct RECT +{ + LONG left; + LONG top; + LONG right; + LONG bottom; +} +alias RECT* PRECT, NPRECT, LPRECT; + +struct PAINTSTRUCT { + HDC hdc; + BOOL fErase; + RECT rcPaint; + BOOL fRestore; + BOOL fIncUpdate; + BYTE rgbReserved[32]; +} +alias PAINTSTRUCT* PPAINTSTRUCT, NPPAINTSTRUCT, LPPAINTSTRUCT; + +export +{ + HDC GetWindowDC(HWND hWnd); + int ReleaseDC(HWND hWnd, HDC hDC); + HDC BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint); + BOOL EndPaint(HWND hWnd, PAINTSTRUCT *lpPaint); + BOOL GetUpdateRect(HWND hWnd, LPRECT lpRect, BOOL bErase); + int GetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase); + int SetWindowRgn(HWND hWnd, HRGN hRgn, BOOL bRedraw); + int GetWindowRgn(HWND hWnd, HRGN hRgn); + int ExcludeUpdateRgn(HDC hDC, HWND hWnd); + BOOL InvalidateRect(HWND hWnd, RECT *lpRect, BOOL bErase); + BOOL ValidateRect(HWND hWnd, RECT *lpRect); + BOOL InvalidateRgn(HWND hWnd, HRGN hRgn, BOOL bErase); + BOOL ValidateRgn(HWND hWnd, HRGN hRgn); + BOOL RedrawWindow(HWND hWnd, RECT *lprcUpdate, HRGN hrgnUpdate, UINT flags); +} + +export +{ + BOOL GetClientRect(HWND hWnd, LPRECT lpRect); + BOOL GetWindowRect(HWND hWnd, LPRECT lpRect); + BOOL AdjustWindowRect(LPRECT lpRect, DWORD dwStyle, BOOL bMenu); + BOOL AdjustWindowRectEx(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle); + HFONT CreateFontA(int, int, int, int, int, DWORD, + DWORD, DWORD, DWORD, DWORD, DWORD, + DWORD, DWORD, LPCSTR); + HFONT CreateFontW(int, int, int, int, int, DWORD, + DWORD, DWORD, DWORD, DWORD, DWORD, + DWORD, DWORD, LPCWSTR); +} + +enum +{ + OUT_DEFAULT_PRECIS = 0, + OUT_STRING_PRECIS = 1, + OUT_CHARACTER_PRECIS = 2, + OUT_STROKE_PRECIS = 3, + OUT_TT_PRECIS = 4, + OUT_DEVICE_PRECIS = 5, + OUT_RASTER_PRECIS = 6, + OUT_TT_ONLY_PRECIS = 7, + OUT_OUTLINE_PRECIS = 8, + OUT_SCREEN_OUTLINE_PRECIS = 9, + + CLIP_DEFAULT_PRECIS = 0, + CLIP_CHARACTER_PRECIS = 1, + CLIP_STROKE_PRECIS = 2, + CLIP_MASK = 0xf, + CLIP_LH_ANGLES = (1<<4), + CLIP_TT_ALWAYS = (2<<4), + CLIP_EMBEDDED = (8<<4), + + DEFAULT_QUALITY = 0, + DRAFT_QUALITY = 1, + PROOF_QUALITY = 2, + + NONANTIALIASED_QUALITY = 3, + ANTIALIASED_QUALITY = 4, + + + DEFAULT_PITCH = 0, + FIXED_PITCH = 1, + VARIABLE_PITCH = 2, + + MONO_FONT = 8, + + + ANSI_CHARSET = 0, + DEFAULT_CHARSET = 1, + SYMBOL_CHARSET = 2, + SHIFTJIS_CHARSET = 128, + HANGEUL_CHARSET = 129, + GB2312_CHARSET = 134, + CHINESEBIG5_CHARSET = 136, + OEM_CHARSET = 255, + + JOHAB_CHARSET = 130, + HEBREW_CHARSET = 177, + ARABIC_CHARSET = 178, + GREEK_CHARSET = 161, + TURKISH_CHARSET = 162, + VIETNAMESE_CHARSET = 163, + THAI_CHARSET = 222, + EASTEUROPE_CHARSET = 238, + RUSSIAN_CHARSET = 204, + + MAC_CHARSET = 77, + BALTIC_CHARSET = 186, + + FS_LATIN1 = 0x00000001L, + FS_LATIN2 = 0x00000002L, + FS_CYRILLIC = 0x00000004L, + FS_GREEK = 0x00000008L, + FS_TURKISH = 0x00000010L, + FS_HEBREW = 0x00000020L, + FS_ARABIC = 0x00000040L, + FS_BALTIC = 0x00000080L, + FS_VIETNAMESE = 0x00000100L, + FS_THAI = 0x00010000L, + FS_JISJAPAN = 0x00020000L, + FS_CHINESESIMP = 0x00040000L, + FS_WANSUNG = 0x00080000L, + FS_CHINESETRAD = 0x00100000L, + FS_JOHAB = 0x00200000L, + FS_SYMBOL = (int)0x80000000L, + + +/* Font Families */ + FF_DONTCARE = (0<<4), /* Don't care or don't know. */ + FF_ROMAN = (1<<4), /* Variable stroke width, serifed. */ + /* Times Roman, Century Schoolbook, etc. */ + FF_SWISS = (2<<4), /* Variable stroke width, sans-serifed. */ + /* Helvetica, Swiss, etc. */ + FF_MODERN = (3<<4), /* Constant stroke width, serifed or sans-serifed. */ + /* Pica, Elite, Courier, etc. */ + FF_SCRIPT = (4<<4), /* Cursive, etc. */ + FF_DECORATIVE = (5<<4), /* Old English, etc. */ + +/* Font Weights */ + FW_DONTCARE = 0, + FW_THIN = 100, + FW_EXTRALIGHT = 200, + FW_LIGHT = 300, + FW_NORMAL = 400, + FW_MEDIUM = 500, + FW_SEMIBOLD = 600, + FW_BOLD = 700, + FW_EXTRABOLD = 800, + FW_HEAVY = 900, + + FW_ULTRALIGHT = FW_EXTRALIGHT, + FW_REGULAR = FW_NORMAL, + FW_DEMIBOLD = FW_SEMIBOLD, + FW_ULTRABOLD = FW_EXTRABOLD, + FW_BLACK = FW_HEAVY, + + PANOSE_COUNT = 10, + PAN_FAMILYTYPE_INDEX = 0, + PAN_SERIFSTYLE_INDEX = 1, + PAN_WEIGHT_INDEX = 2, + PAN_PROPORTION_INDEX = 3, + PAN_CONTRAST_INDEX = 4, + PAN_STROKEVARIATION_INDEX = 5, + PAN_ARMSTYLE_INDEX = 6, + PAN_LETTERFORM_INDEX = 7, + PAN_MIDLINE_INDEX = 8, + PAN_XHEIGHT_INDEX = 9, + + PAN_CULTURE_LATIN = 0, +} + +struct RGBQUAD { + BYTE rgbBlue; + BYTE rgbGreen; + BYTE rgbRed; + BYTE rgbReserved; +} +alias RGBQUAD* LPRGBQUAD; + +struct BITMAPINFOHEADER +{ + DWORD biSize; + LONG biWidth; + LONG biHeight; + WORD biPlanes; + WORD biBitCount; + DWORD biCompression; + DWORD biSizeImage; + LONG biXPelsPerMeter; + LONG biYPelsPerMeter; + DWORD biClrUsed; + DWORD biClrImportant; +} +alias BITMAPINFOHEADER* LPBITMAPINFOHEADER, PBITMAPINFOHEADER; + +struct BITMAPINFO { + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[1]; +} +alias BITMAPINFO* LPBITMAPINFO, PBITMAPINFO; + +struct PALETTEENTRY { + BYTE peRed; + BYTE peGreen; + BYTE peBlue; + BYTE peFlags; +} +alias PALETTEENTRY* PPALETTEENTRY, LPPALETTEENTRY; + +/* Pixel format descriptor */ +struct PIXELFORMATDESCRIPTOR +{ + WORD nSize; + WORD nVersion; + DWORD dwFlags; + BYTE iPixelType; + BYTE cColorBits; + BYTE cRedBits; + BYTE cRedShift; + BYTE cGreenBits; + BYTE cGreenShift; + BYTE cBlueBits; + BYTE cBlueShift; + BYTE cAlphaBits; + BYTE cAlphaShift; + BYTE cAccumBits; + BYTE cAccumRedBits; + BYTE cAccumGreenBits; + BYTE cAccumBlueBits; + BYTE cAccumAlphaBits; + BYTE cDepthBits; + BYTE cStencilBits; + BYTE cAuxBuffers; + BYTE iLayerType; + BYTE bReserved; + DWORD dwLayerMask; + DWORD dwVisibleMask; + DWORD dwDamageMask; +} +alias PIXELFORMATDESCRIPTOR* PPIXELFORMATDESCRIPTOR, LPPIXELFORMATDESCRIPTOR; + + +export +{ + BOOL RoundRect(HDC, int, int, int, int, int, int); + BOOL ResizePalette(HPALETTE, UINT); + int SaveDC(HDC); + int SelectClipRgn(HDC, HRGN); + int ExtSelectClipRgn(HDC, HRGN, int); + int SetMetaRgn(HDC); + HGDIOBJ SelectObject(HDC, HGDIOBJ); + HPALETTE SelectPalette(HDC, HPALETTE, BOOL); + COLORREF SetBkColor(HDC, COLORREF); + int SetBkMode(HDC, int); + LONG SetBitmapBits(HBITMAP, DWORD, void *); + UINT SetBoundsRect(HDC, RECT *, UINT); + int SetDIBits(HDC, HBITMAP, UINT, UINT, void *, BITMAPINFO *, UINT); + int SetDIBitsToDevice(HDC, int, int, DWORD, DWORD, int, + int, UINT, UINT, void *, BITMAPINFO *, UINT); + DWORD SetMapperFlags(HDC, DWORD); + int SetGraphicsMode(HDC hdc, int iMode); + int SetMapMode(HDC, int); + HMETAFILE SetMetaFileBitsEx(UINT, BYTE *); + UINT SetPaletteEntries(HPALETTE, UINT, UINT, PALETTEENTRY *); + COLORREF SetPixel(HDC, int, int, COLORREF); + BOOL SetPixelV(HDC, int, int, COLORREF); + BOOL SetPixelFormat(HDC, int, PIXELFORMATDESCRIPTOR *); + int SetPolyFillMode(HDC, int); + BOOL StretchBlt(HDC, int, int, int, int, HDC, int, int, int, int, DWORD); + BOOL SetRectRgn(HRGN, int, int, int, int); + int StretchDIBits(HDC, int, int, int, int, int, int, int, int, + void *, BITMAPINFO *, UINT, DWORD); + int SetROP2(HDC, int); + int SetStretchBltMode(HDC, int); + UINT SetSystemPaletteUse(HDC, UINT); + int SetTextCharacterExtra(HDC, int); + COLORREF SetTextColor(HDC, COLORREF); + UINT SetTextAlign(HDC, UINT); + BOOL SetTextJustification(HDC, int, int); + BOOL UpdateColors(HDC); +} + +/* Text Alignment Options */ +enum +{ + TA_NOUPDATECP = 0, + TA_UPDATECP = 1, + + TA_LEFT = 0, + TA_RIGHT = 2, + TA_CENTER = 6, + + TA_TOP = 0, + TA_BOTTOM = 8, + TA_BASELINE = 24, + + TA_RTLREADING = 256, + TA_MASK = (TA_BASELINE+TA_CENTER+TA_UPDATECP+TA_RTLREADING), +} + +struct POINT +{ + LONG x; + LONG y; +} +alias POINT* PPOINT, NPPOINT, LPPOINT; + + +export +{ + BOOL MoveToEx(HDC, int, int, LPPOINT); + BOOL TextOutA(HDC, int, int, LPCSTR, int); + BOOL TextOutW(HDC, int, int, LPCWSTR, int); +} + +export void PostQuitMessage(int nExitCode); +export LRESULT DefWindowProcA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +export HMODULE GetModuleHandleA(LPCSTR lpModuleName); + +alias LRESULT (* WNDPROC)(HWND, UINT, WPARAM, LPARAM); + +struct WNDCLASSEXA { + UINT cbSize; + /* Win 3.x */ + UINT style; + WNDPROC lpfnWndProc; + int cbClsExtra; + int cbWndExtra; + HINSTANCE hInstance; + HICON hIcon; + HCURSOR hCursor; + HBRUSH hbrBackground; + LPCSTR lpszMenuName; + LPCSTR lpszClassName; + /* Win 4.0 */ + HICON hIconSm; +} +alias WNDCLASSEXA* PWNDCLASSEXA, NPWNDCLASSEXA, LPWNDCLASSEXA; + + +struct WNDCLASSA { + UINT style; + WNDPROC lpfnWndProc; + int cbClsExtra; + int cbWndExtra; + HINSTANCE hInstance; + HICON hIcon; + HCURSOR hCursor; + HBRUSH hbrBackground; + LPCSTR lpszMenuName; + LPCSTR lpszClassName; +} +alias WNDCLASSA* PWNDCLASSA, NPWNDCLASSA, LPWNDCLASSA; +alias WNDCLASSA WNDCLASS; + +/* + * Window Styles + */ +enum +{ + WS_OVERLAPPED = 0x00000000, + WS_POPUP = (int)0x80000000, + WS_CHILD = 0x40000000, + WS_MINIMIZE = 0x20000000, + WS_VISIBLE = 0x10000000, + WS_DISABLED = 0x08000000, + WS_CLIPSIBLINGS = 0x04000000, + WS_CLIPCHILDREN = 0x02000000, + WS_MAXIMIZE = 0x01000000, + WS_CAPTION = 0x00C00000, /* WS_BORDER | WS_DLGFRAME */ + WS_BORDER = 0x00800000, + WS_DLGFRAME = 0x00400000, + WS_VSCROLL = 0x00200000, + WS_HSCROLL = 0x00100000, + WS_SYSMENU = 0x00080000, + WS_THICKFRAME = 0x00040000, + WS_GROUP = 0x00020000, + WS_TABSTOP = 0x00010000, + + WS_MINIMIZEBOX = 0x00020000, + WS_MAXIMIZEBOX = 0x00010000, + + WS_TILED = WS_OVERLAPPED, + WS_ICONIC = WS_MINIMIZE, + WS_SIZEBOX = WS_THICKFRAME, + +/* + * Common Window Styles + */ + WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX), + WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW, + WS_POPUPWINDOW = (WS_POPUP | WS_BORDER | WS_SYSMENU), + WS_CHILDWINDOW = (WS_CHILD), +} + +/* + * Class styles + */ +enum +{ + CS_VREDRAW = 0x0001, + CS_HREDRAW = 0x0002, + CS_KEYCVTWINDOW = 0x0004, + CS_DBLCLKS = 0x0008, + CS_OWNDC = 0x0020, + CS_CLASSDC = 0x0040, + CS_PARENTDC = 0x0080, + CS_NOKEYCVT = 0x0100, + CS_NOCLOSE = 0x0200, + CS_SAVEBITS = 0x0800, + CS_BYTEALIGNCLIENT = 0x1000, + CS_BYTEALIGNWINDOW = 0x2000, + CS_GLOBALCLASS = 0x4000, + + + CS_IME = 0x00010000, +} + +export +{ + HICON LoadIconA(HINSTANCE hInstance, LPCSTR lpIconName); + HICON LoadIconW(HINSTANCE hInstance, LPCWSTR lpIconName); + HCURSOR LoadCursorA(HINSTANCE hInstance, LPCSTR lpCursorName); + HCURSOR LoadCursorW(HINSTANCE hInstance, LPCWSTR lpCursorName); +} + +const LPSTR IDI_APPLICATION = cast(LPSTR)(32512); +const LPSTR IDC_CROSS = cast(LPSTR)(32515); + +/* + * Color Types + */ +enum +{ + CTLCOLOR_MSGBOX = 0, + CTLCOLOR_EDIT = 1, + CTLCOLOR_LISTBOX = 2, + CTLCOLOR_BTN = 3, + CTLCOLOR_DLG = 4, + CTLCOLOR_SCROLLBAR = 5, + CTLCOLOR_STATIC = 6, + CTLCOLOR_MAX = 7, + + COLOR_SCROLLBAR = 0, + COLOR_BACKGROUND = 1, + COLOR_ACTIVECAPTION = 2, + COLOR_INACTIVECAPTION = 3, + COLOR_MENU = 4, + COLOR_WINDOW = 5, + COLOR_WINDOWFRAME = 6, + COLOR_MENUTEXT = 7, + COLOR_WINDOWTEXT = 8, + COLOR_CAPTIONTEXT = 9, + COLOR_ACTIVEBORDER = 10, + COLOR_INACTIVEBORDER = 11, + COLOR_APPWORKSPACE = 12, + COLOR_HIGHLIGHT = 13, + COLOR_HIGHLIGHTTEXT = 14, + COLOR_BTNFACE = 15, + COLOR_BTNSHADOW = 16, + COLOR_GRAYTEXT = 17, + COLOR_BTNTEXT = 18, + COLOR_INACTIVECAPTIONTEXT = 19, + COLOR_BTNHIGHLIGHT = 20, + + + COLOR_3DDKSHADOW = 21, + COLOR_3DLIGHT = 22, + COLOR_INFOTEXT = 23, + COLOR_INFOBK = 24, + + COLOR_DESKTOP = COLOR_BACKGROUND, + COLOR_3DFACE = COLOR_BTNFACE, + COLOR_3DSHADOW = COLOR_BTNSHADOW, + COLOR_3DHIGHLIGHT = COLOR_BTNHIGHLIGHT, + COLOR_3DHILIGHT = COLOR_BTNHIGHLIGHT, + COLOR_BTNHILIGHT = COLOR_BTNHIGHLIGHT, +} + +const int CW_USEDEFAULT = (int)0x80000000; +/* + * Special value for CreateWindow, et al. + */ +const HWND HWND_DESKTOP = ((HWND)0); + + +export ATOM RegisterClassA(WNDCLASSA *lpWndClass); + +export HWND CreateWindowExA( + DWORD dwExStyle, + LPCSTR lpClassName, + LPCSTR lpWindowName, + DWORD dwStyle, + int X, + int Y, + int nWidth, + int nHeight, + HWND hWndParent , + HMENU hMenu, + HINSTANCE hInstance, + LPVOID lpParam); + + +HWND CreateWindowA( + LPCSTR lpClassName, + LPCSTR lpWindowName, + DWORD dwStyle, + int X, + int Y, + int nWidth, + int nHeight, + HWND hWndParent , + HMENU hMenu, + HINSTANCE hInstance, + LPVOID lpParam) +{ + return CreateWindowExA(0, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); +} + +/* + * Message structure + */ +struct MSG { + HWND hwnd; + UINT message; + WPARAM wParam; + LPARAM lParam; + DWORD time; + POINT pt; +} +alias MSG* PMSG, NPMSG, LPMSG; + +export +{ + BOOL GetMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax); + BOOL TranslateMessage(MSG *lpMsg); + LONG DispatchMessageA(MSG *lpMsg); + BOOL PeekMessageA(MSG *lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg); + HWND GetFocus(); +} + }