libk  Check-in [175dc46a91]

Overview
Comment:update kcore docs, fix bad type names and dumb logic
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 175dc46a91d51ec73c04b30f7d35e880d67e373502ff69c9980d7a644d1a8cbb
User & Date: lexi on 2019-07-26 10:52:44
Other Links: manifest | tags
Context
2019-07-26
21:56
add kmath module stub check-in: 85a8c60bd2 user: lexi tags: trunk
10:52
update kcore docs, fix bad type names and dumb logic check-in: 175dc46a91 user: lexi tags: trunk
09:51
major update. fix ridiculous old type size determination mechanism. mmap is still broken and i'm not sure why; the syscall does not appear to be going through correctly - see posix_mmap, kmheapa, and kcore/testbin.exe.fn check-in: 6479e060a3 user: lexi tags: trunk
Changes

Modified kcore/kcore.md from [28cd81b166] to [a610cab74a].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


22







23
24
25
26
27
28
29
..
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# kcore
**kcore** is the foundation for the rest of libk. it defines types and structs that are needed by every program, and provides the stub that launches a program's "entry" function. unlike the non-core modules, kcore definitions simply use the prefix `k-`.

## entry
when using libk, your program's entry point will not be the `int main(int,char**)` function that libc opens into. libk will call the function `stat entry(kenv)` instead. like libc, the value returned by `entry` will be returned to the host platform.

## types
kcore contains fixed-width integer types (in <k/type.h>). note that the availability of each depends on your platform; compilation will fail if e.g. you try to use a u64 or a u128 on a 32-bit platform, so where exact lengths are not required, you may wish to use the built-in C types instead.

 * `u8` - an unsigned 8-bit integer
 * `s8` - a signed 8-bit integer
 * `u16` - an unsigned 16-bit integer
 * `s16` - a signed 16-bit integer
 * `u32` - an unsigned 32-bit integer
 * `s32` - a signed 32-bit integer
 * `u64` - an unsigned 64-bit integer
 * `s64` - a signed 64-bit integer
 * `u128` - an unsigned 128-bit integer
 * `s128` - a signed 128-bit integer
 * `word` - an unsigned integer of platform word-length (e.g. 32 bits on x86.32; 64 on x86.64)
 * `sword` - a signed integer of platform word-length (e.g. 32 bits on x86.32; 64 on x86.64)


 * `stat` - the type of process return values expected by the platform (usually u8 on linux)








### struct kenv
`kenv` is a struct that encompasses the environment the program was launched in.
 * `kiochan std` - a stereo IO channel for reading and writing to and from stdout.
 * `kiochan err` - a mono IO channel for writing to stderr.
 * `kvar* vars` - a pointer into the program's environment

................................................................................
 * `kstr val` - the value of an environment variable
 * `char* platform` - a pointer into the platform's underlying representation

## functions

### kstop()

`noreturn void kstop(stat)` terminates the program immediately, returning the specified integer to the OS as an exit status. the type of integer it takes depends on your operating system. consider using the `enum kbad` defines in `<k/magic.h>`, which are designed to standardize exit statuses across different software and are synchronized with a FreeBSD effort to do the same (`<sysexit.h`).

## definitions

kcore is the only module that defines any terms outside the k- namespace. the only terms it so defines are native C terms like `noreturn`, which are implemented as keywords in a reserved namespace (`_` followed by an uppercase letter; in this case, `_Noreturn`). macros are then defined in new headers for the "natural" version of the term in order to avoid breaking older code. examples of this technique are `<stdbool.h>` and `<stdnoreturn.h>`. since libk is designed for new, modern code, breaking old code isn't a concern, and we turn on all these new keywords as soon as you load `<k/core.h>`.

libk attempts to find the best possible definition and implementation for these various types and keywords, abstracting across different C versions and dialects. you can go ahead and use the normal version of the keywords (e.g. `noreturn`, `bool`) no matter what kind of compiler you're using and you're guaranteed that as long as you don't go fiddling with undefined- or implementation behavior, your code will behave the same every time. at worst, it may lack a few optimizations or warnings.








|

|
|






|
|
|
|
>
>

>
>
>
>
>
>
>







 







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# kcore
**kcore** is the foundation for the rest of libk. it defines types and structs that are needed by every program, and provides the stub that launches a program's "entry" function. unlike the non-core modules, kcore definitions simply use the prefix `k-`.

## entry
when using libk, your program's entry point will not be the `int main(int,char**)` function that libc opens into. libk will call the function `stat entry(kenv)` instead. like libc, the value returned by `entry` will be returned to the host platform.

## types
kcore contains fixed-width integer types (in `<k/type.h>`). these types are present on every platform. if a platform has a type with a given bit length, it will be used, otherwise, the type will round to the largest available type (except `u8` and `s8`). if you need to be absolutely certain that a type is the appropriate bit length, use sizeof to check its length in a conditional or static assert, for instance `if (sizeof(u16) == 16 / kc_byte_bits)`.

 * `u8` - an unsigned 8-bit integer, or the smallest available unsigned type
 * `s8` - a signed 8-bit integer, or the smallest available signed type
 * `u16` - an unsigned 16-bit integer
 * `s16` - a signed 16-bit integer
 * `u32` - an unsigned 32-bit integer
 * `s32` - a signed 32-bit integer
 * `u64` - an unsigned 64-bit integer
 * `s64` - a signed 64-bit integer
 * `u128` - an unsigned 128-bit integer (uses GCC and clang extensions to offer 128-bit integer support on x86-64)
 * `s128` - a signed 128-bit integer (ibid)
 * `ubig` - the largest available unsigned integer type
 * `sbig` - the largest available signed integer type. note: ubig and sbig really are the *largest* possible integer types not just the largest native types - if your compiler has extensions for 128-bit types on your arch (as GCC and clang do on x86-64), ubig and sbig will be 128 bits long even if your system word length is less than 128. so only use ubig/sbig if you really, really mean it.
 * `ubyte` - the smallest available unsigned integer type besides \_Bool
 * `sbyte` - the smallest available signed integer type besides \_Bool
 * `stat` - the type of process return values expected by the platform (usually u8 on linux)
 * `sz` - a type large enough to cover a platform's entire address space (libc equivalent size_t)
 * `offset` - a type that can contain the difference between two pointers (libc equivalent ptrdiff_t)

## constants
in `<k/type.h>`, every type has an associated min and max constant, containing the smallest and largest value that type can hold on your system. these can be access by suffixing a type with `_min` and `_max`, respectively. min and max values of native types can be accessed with the `kc_[us]` prefix - for instance, the minimum value of `signed long` is `kc_slong_min`. (`long long` can be referenced as `llong`).

 * `byte_bits` - the bit length of ubyte, sbyte, and char; that is, the number of bits in the type `sizeof` measures things in terms of. `sizeof(type) * byte_bits` will always return the number of bits in a type.

### struct kenv
`kenv` is a struct that encompasses the environment the program was launched in.
 * `kiochan std` - a stereo IO channel for reading and writing to and from stdout.
 * `kiochan err` - a mono IO channel for writing to stderr.
 * `kvar* vars` - a pointer into the program's environment

................................................................................
 * `kstr val` - the value of an environment variable
 * `char* platform` - a pointer into the platform's underlying representation

## functions

### kstop()

`noreturn void kstop(stat)` terminates the program immediately, returning the specified integer to the OS as an exit status. the type of integer it takes depends on your operating system. consider using the `enum kbad` defines in `<k/magic.h>`, which are designed to standardize exit statuses across different software and are synchronized with a FreeBSD effort to do the same (`<sysexit.h>`).

## definitions

kcore is the only module that defines any terms outside the k- namespace. the only terms it so defines are native C terms like `noreturn`, which are implemented as keywords in a reserved namespace (`_` followed by an uppercase letter; in this case, `_Noreturn`). macros are then defined in new headers for the "natural" version of the term in order to avoid breaking older code. examples of this technique are `<stdbool.h>` and `<stdnoreturn.h>`. since libk is designed for new, modern code, breaking old code isn't a concern, and we turn on all these new keywords as soon as you load `<k/core.h>`.

libk attempts to find the best possible definition and implementation for these various types and keywords, abstracting across different C versions and dialects. you can go ahead and use the normal version of the keywords (e.g. `noreturn`, `bool`) no matter what kind of compiler you're using and you're guaranteed that as long as you don't go fiddling with undefined- or implementation behavior, your code will behave the same every time. at worst, it may lack a few optimizations or warnings.

Modified kcore/type.h.m from [a9978dfd3a] to [def2d33e25].

3
4
5
6
7
8
9
10
11
12
13
14

15
16
17
18
19
20
21
22


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42



43
44
45
46
47
48




49
50
51
52
53
54
55
56
57
58
59
60
61
62
63





























64
65
66
67

68
69
70
71
72
73

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92






93
94
95
96
97
98
99
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
--- this file gathers information on the environment it's
--- being compiled in, defining types that our code
--- needs. it will be emitted as <k/type.h>.
--- vim: ft=c
#ifndef KItype
#define KItype

typedef unsigned char      kc_uint_min;
typedef   signed char      kc_sint_min;
typedef unsigned long long kc_uint_max;
typedef   signed long long kc_sint_max;


[ifdef type_bit8]
	typedef unsigned [type_bit8] u8;
	typedef   signed [type_bit8] s8;
[else]
	typedef kc_uint_min u8;
	typedef	kc_sint_min s8;
[endif]



[ifdef type_bit16]
	typedef unsigned [type_bit16] u16;
	typedef   signed [type_bit16] s16;
[else]
	typedef kc_uint_max u16;
	typedef	kc_sint_max s16;
[endif]

[ifdef type_bit32]
	typedef unsigned [type_bit32] u32;
	typedef   signed [type_bit32] s32;
[else]
	typedef kc_uint_max u32;
	typedef	kc_sint_max s32;
[endif]

[ifdef type_bit64]
	typedef unsigned [type_bit64] u64;
	typedef   signed [type_bit64] s64;
	[ifndef type_bit128]



#		if defined(__GNUC__) || defined(__clang__)
			typedef unsigned __int128_t u128;
			typedef   signed __int128_t s128;
#		else
			typedef kc_uint_max u128;
			typedef kc_sint_max s128;




#		endif
	[endif]
[else]
	typedef kc_uint_max u64;
	typedef	kc_sint_max s64;

	typedef kc_uint_max u128;
	typedef	kc_sint_max s128;
[endif]

[ifdef type_bit128]
	typedef unsigned [type_bit128] u128;
	typedef   signed [type_bit128] s128;
[endif]






























enum /* max-min values of each type */ {
	/* assuming two's complement. TODO: check math */
	/* TODO: figure out how to calc min */
	kc_byte_bits = [byte_bits],

	  u8_min = 0,   u8_max = ((u8)-1),
	 u16_min = 0,  u16_max = ((u16)-1),
	 u32_min = 0,  u32_max = ((u32)-1),
	 u64_min = 0,  u32_max = ((u64)-1),
	u128_min = 0, u128_max = ((u128)-1),


	[define merge: $1$2]
	[define [sspec type]:
		[merge [type],_min] = 0 - ((1 << sizeof([type]) * kc_byte_bits) / 2),
		[merge [type],_max] =      (1 << sizeof([type]) * kc_byte_bits) / 2 - 1]

	[sspec s8], [sspec s16], [sspec s32],
	[sspec s64], [sspec s128],

	kc_min_uchar  = 0, kc_max_uchar  = [type_max_u_char],
	kc_min_ushort = 0, kc_max_ushort = [type_max_u_short],
	kc_min_uint   = 0, kc_max_uint   = [type_max_u_int],
	kc_min_ulong  = 0, kc_max_ulong  = [type_max_u_long],
	kc_min_ullong = 0, kc_max_ullong = [type_max_u_llong],

	kc_min_schar  = [type_min_s_char],  kc_max_schar  = [type_max_s_char],
	kc_min_sshort = [type_min_s_short], kc_max_sshort = [type_max_s_short],
	kc_min_sint   = [type_min_s_int],   kc_max_sint   = [type_max_s_int],
	kc_min_slong  = [type_min_s_long],  kc_max_slong  = [type_max_s_long],
	kc_min_sllong = [type_min_s_llong], kc_max_sllong = [type_max_s_llong],






};

[ifdef type_sz]
	typedef [type_sz] sz;
[else]
#	ifdef __cplusplus
 	   /* C++ gives us a clean, standardized way to do this */
................................................................................
#		if defined(__GNUC__) || defined(__clang__)
			typedef __typeof__ (sizeof(char)) sz;
#		else
			/* we're stumped - set sz to the safest possible value under
			 * the circumstances, and warn the user. */
#			warning no authoritative sz (size_t) type definition \
			        available; defaulting to largest unsigned integer type
			typedef kc_uint_max sz;
#		endif
#	endif
[endif]

[ifdef type_offset]
	typedef [type_offset] offset;
[else]
................................................................................
#		if defined(__GNUC__) || defined(__clang__)
			typedef __typeof__ (((void*)10) - ((void*)5)) offset;
#		else
			/* no dice - set offset to the safest possible value under
			 * the circumstances, and warn the user. */
#			warning no authoritative offset (ptrdiff_t) type definition \
			        available; defaulting to largest unsigned integer type
			typedef kc_sint_max offset;
#		endif
#	endif
[endif]

// exit status integer types - pls use kbad in <k/magic.h> instead
[if [target_posix] == yes]
	/* by convention, posix return values are 8-bit,







|
|
|
|
<
>
|
|
|
|
|
<
<
|
>
>
|
<
<
<
<
<
<

<
<
<
<
<
<
<
<




>
>
>




|
|
>
>
>
>



|
|

|
|







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<
<
|
>






>








|
|
|
|
|

|
|
|
|
|
>
>
>
>
>
>







 







|







 







|







3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19


20
21
22
23






24








25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86


87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
--- this file gathers information on the environment it's
--- being compiled in, defining types that our code
--- needs. it will be emitted as <k/type.h>.
--- vim: ft=c
#ifndef KItype
#define KItype

/* we define 64-bit types first due to an oddity in how
 * 128-bit types are handled: we want kc_?big to reference
 * the absolute largest type available to the compiler,
 * but in some cases, 128-bits may not be among the

 * standard C types despite being supported by the
 * compiler. to work around this, we first check whether
 * 64-bit types are available (__int128_t only works on
 * 64-bit systems) and then whether the compiler is one
 * that supports the 128-bit extension - but only if a
 * native 128-bit type is not available.


 * 
 * once this is done, we can be certain that u128 will
 * reference the largest available integer type and can
 * safely define kc_?big by reference to it. */















[ifdef type_bit64]
	typedef unsigned [type_bit64] u64;
	typedef   signed [type_bit64] s64;
	[ifndef type_bit128]
	/* even if no native type is 128 bits long, clang and
	 * gcc have extensions to support 128 bit arithmetic
	 * on 64-bit hardware */
#		if defined(__GNUC__) || defined(__clang__)
			typedef unsigned __int128_t u128;
			typedef   signed __int128_t s128;
#		else
			/* if we don't have access to that extension
			 * or native 128-bit types, then we just use
			 * the largest native type specified in the
			 * C standard */
			typedef unsigned long long u128;
			typedef   signed long long s128;
#		endif
	[endif]
[else]
	typedef unsigned long long u64;
	typedef   signed long long s64;

	typedef u64 u128;
	typedef	s64 s128;
[endif]

[ifdef type_bit128]
	typedef unsigned [type_bit128] u128;
	typedef   signed [type_bit128] s128;
[endif]

typedef unsigned char ubyte;
typedef   signed char sbyte;
typedef          u128 ubig;
typedef          s128 sbig;

[ifdef type_bit8]
	typedef unsigned [type_bit8] u8;
	typedef   signed [type_bit8] s8;
[else]
	typedef ubyte u8;
	typedef	sbyte s8;
[endif]

[ifdef type_bit16]
	typedef unsigned [type_bit16] u16;
	typedef   signed [type_bit16] s16;
[else]
	typedef ubig u16;
	typedef	sbig s16;
[endif]

[ifdef type_bit32]
	typedef unsigned [type_bit32] u32;
	typedef   signed [type_bit32] s32;
[else]
	typedef ubig u32;
	typedef	sbig s32;
[endif]

enum /* max-min values of each type */ {


	byte_bits = [byte_bits],

	  u8_min = 0,   u8_max = ((u8)-1),
	 u16_min = 0,  u16_max = ((u16)-1),
	 u32_min = 0,  u32_max = ((u32)-1),
	 u64_min = 0,  u32_max = ((u64)-1),
	u128_min = 0, u128_max = ((u128)-1),

	/* assuming two's complement. TODO: check math */
	[define merge: $1$2]
	[define [sspec type]:
		[merge [type],_min] = 0 - ((1 << sizeof([type]) * kc_byte_bits) / 2),
		[merge [type],_max] =      (1 << sizeof([type]) * kc_byte_bits) / 2 - 1]

	[sspec s8], [sspec s16], [sspec s32],
	[sspec s64], [sspec s128],

	kc_uchar_min  = 0, kc_uchar_max  = [type_max_u_char],
	kc_ushort_min = 0, kc_ushort_max = [type_max_u_short],
	kc_uint_min   = 0, kc_uint_max   = [type_max_u_int],
	kc_ulong_min  = 0, kc_ulong_max  = [type_max_u_long],
	kc_ullong_min = 0, kc_ullong_max = [type_max_u_llong],

	kc_schar_min  = [type_min_s_char],  kc_schar_max  = [type_max_s_char],
	kc_sshort_min = [type_min_s_short], kc_sshort_max = [type_max_s_short],
	kc_sint_min   = [type_min_s_int],   kc_sint_max   = [type_max_s_int],
	kc_slong_min  = [type_min_s_long],  kc_slong_max  = [type_max_s_long],
	kc_sllong_min = [type_min_s_llong], kc_sllong_max = [type_max_s_llong],

	ubig_min = u128_min, ubig_max = u128_max,
	sbig_min = s128_min, sbig_max = s128_max,

	ubyte_min = kc_uchar_min, ubyte_max = kc_uchar_max,
	sbyte_min = kc_schar_min, sbyte_max = kc_schar_max,
};

[ifdef type_sz]
	typedef [type_sz] sz;
[else]
#	ifdef __cplusplus
 	   /* C++ gives us a clean, standardized way to do this */
................................................................................
#		if defined(__GNUC__) || defined(__clang__)
			typedef __typeof__ (sizeof(char)) sz;
#		else
			/* we're stumped - set sz to the safest possible value under
			 * the circumstances, and warn the user. */
#			warning no authoritative sz (size_t) type definition \
			        available; defaulting to largest unsigned integer type
			typedef ubig sz;
#		endif
#	endif
[endif]

[ifdef type_offset]
	typedef [type_offset] offset;
[else]
................................................................................
#		if defined(__GNUC__) || defined(__clang__)
			typedef __typeof__ (((void*)10) - ((void*)5)) offset;
#		else
			/* no dice - set offset to the safest possible value under
			 * the circumstances, and warn the user. */
#			warning no authoritative offset (ptrdiff_t) type definition \
			        available; defaulting to largest unsigned integer type
			typedef sbig offset;
#		endif
#	endif
[endif]

// exit status integer types - pls use kbad in <k/magic.h> instead
[if [target_posix] == yes]
	/* by convention, posix return values are 8-bit,