Index: mod/kcli/testbin.exe.c ================================================================== --- mod/kcli/testbin.exe.c +++ mod/kcli/testbin.exe.c @@ -6,9 +6,9 @@ kcli_set testbin = { "testbin", "1.0.0", e.args, e.argc, "this is a test of the kcli module", }; - kcli_usage(testbin, e.err); + kcond c = kcli_usage(testbin, e.err); - return 0; + return c; } Index: mod/kcli/usage.fn.c ================================================================== --- mod/kcli/usage.fn.c +++ mod/kcli/usage.fn.c @@ -1,56 +1,20 @@ #include #include #include -typedef struct buffer { - char* cur; - kiochan channel; - sz run; - char buf []; -} buffer; - -static buffer* -buffer_new(void* mem, kiochan channel, sz run) { - buffer* r = mem; - r -> cur = r -> buf; - r -> channel = channel; - r -> run = run; - return r; -} - -static kiocond -buffer_flush(buffer* b) { - ksraw str = {b -> cur - b -> buf, b -> buf}; - b -> cur = b -> buf; - return kiosend(b -> channel, str, null); -} - -static kcond -buffer_send(buffer* b, const char* str) { - ksmut buf = { b->run - (b->cur - b->buf), b->cur }; - ksraw src = { 0, str }; - kscond sc = kscp(src, buf, &src.size); - if (sc != kscond_ok) return sc; - - b->cur += src.size; - if (b->cur >= (b->buf + b->run)) { - return buffer_flush(b); - } else return kiocond_ok; -} - kcond kcli_usage(kcli_set prg, kiochan ch) { - ubyte buf_space [sizeof(buffer) + 256]; - buffer* out = buffer_new(buf_space, ch, 256); + ubyte buf_space [sizeof(ksbuf) + 256]; + ksbuf* out = ksbufmk(buf_space, ch, 256); const char* msg [] = { prg.name, " v", prg.version, "\n\n", prg.desc, "\n\n", }; - for (sz i = 0; i != sizeof msg / sizeof msg[0]; ++ i) { - kcond c = buffer_send(out, msg[i]); - if (!kokay(c)) return c; + for (sz i = 0; i != Kmsz(msg); ++ i) { + ksraw str = { 0, msg[i] }; + kcond c = ksbufput(out, str); } - return buffer_flush(out); + return ksbufflush(out); } Index: mod/kcore/core.h ================================================================== --- mod/kcore/core.h +++ mod/kcore/core.h @@ -1,28 +1,19 @@ #ifndef KIcore #define KIcore + #include -#include -#include -#include -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct kvar { - ksraw name; - ksraw val; - char* platform; -} kvar; - -typedef struct kenv { - kiochan std; - kiochan err; - sz argc; const char** args; - sz varc; kvar* vars; -} kenv; +typedef u16 kcond; +/* this will probably not need to be altered, + * as libk sports a modest number of modules, + * and there are few enough error conditions + * in each that 16-bit address space should + * be more than enough for the foreseeable + * future. however if that changes, altering + * the definition here will effect all the + * necessary changes throughout the library */ /* i'm really sorry okay */ typedef #if (__STDC_VERSION__ >= 199901L) _Bool bool; @@ -38,10 +29,12 @@ } #if !(__STDC_VERSION__ >= 199901L) bool /* } bool ; */ #endif ; + +bool kokay(kcond); #ifndef KFclean # include # define Kokay(cond) (((cond) % kglobal_module_offset) == 0) # if (__STDC_VERSION__ >= 199901L) ||\ @@ -148,30 +141,40 @@ _Noreturn void kstop(stat_long code); #else void kstop(stat_long code); #endif -typedef u16 kcond; -/* this will probably not need to be altered, - * as libk sports a modest number of modules, - * and there are few enough error conditions - * in each that 16-bit address space should - * be more than enough for the foreseeable - * future. however if that changes, altering - * the definition here will effect all the - * necessary changes throughout the library */ -bool kokay(kcond); - typedef struct kerror { const char* module_name, * module_desc, * error_string; kcond cond; } kerror; + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kvar { + ksraw name; + ksraw val; + char* platform; +} kvar; + +typedef struct kenv { + kiochan std; + kiochan err; + sz argc; const char** args; + sz varc; kvar* vars; +} kenv; kerror kexplain(kcond); #ifdef __cplusplus } #endif #endif Index: mod/kio/io.h.m ================================================================== --- mod/kio/io.h.m +++ mod/kio/io.h.m @@ -6,11 +6,10 @@ * structures. it is the same for all platforms. * platform-specific code is found in the *.platform.h * files. */ -#include #include #include #ifdef __cplusplus extern "C" { @@ -74,10 +73,12 @@ kiocond_fail_over_quota, kiocond_fail_pfault, kiocond_fail_too_big, kiocond_fail_stream_mismatch, } kiocond; + +#include kiocond kiosend(kiochan, ksraw, sz*); // send data to a channel kiocond kiosendall(kiochan, ksraw); // keep sending data to a channel until it's all sent kiocond kiorecv(kiochan, ksraw*); // receive data from a channel kmptr kiorecvall(kiochan, kmcell*, kmkind); // automatically allocate a bufer for a channel ADDED mod/kstr/bufflush.fn.c Index: mod/kstr/bufflush.fn.c ================================================================== --- mod/kstr/bufflush.fn.c +++ mod/kstr/bufflush.fn.c @@ -0,0 +1,10 @@ +#include +#include +#include + +kiocond +ksbufflush(ksbuf* b) { + ksraw str = {b -> cur - b -> buf, b -> buf}; + b -> cur = b -> buf; + return kiosend(b -> channel, str, null); +} ADDED mod/kstr/bufmk.fn.c Index: mod/kstr/bufmk.fn.c ================================================================== --- mod/kstr/bufmk.fn.c +++ mod/kstr/bufmk.fn.c @@ -0,0 +1,11 @@ +#include +#include + +ksbuf* +ksbufmk (void* where, kiochan channel, sz run) { + ksbuf* r = where; + r -> cur = r -> buf; + r -> channel = channel; + r -> run = run; + return r; +} ADDED mod/kstr/bufput.fn.c Index: mod/kstr/bufput.fn.c ================================================================== --- mod/kstr/bufput.fn.c +++ mod/kstr/bufput.fn.c @@ -0,0 +1,62 @@ +#include +#include +#include + +kcond +ksbufput(ksbuf* b, ksraw src) { + ksmut dest = { b->run - (b->cur - b->buf), b->cur }; + /* there are a number of scenarios we need to account for: + * + * - the ksraw has an unknown size. in this case, we + * determine the size with kssz and proceed according to + * the following rules. we could also count the string + * as we copy, taking advantage of kscp's behavior on + * strings of unknown length, but this would drastically + * complicate the code for an almost certainly negligible + * gain in performance. + * + * - the ksraw has a known size that is shorter than the + * remaining space in the buffer. in this case, we copy + * it into the buffer and return the result of the copy. + * + * - the ksraw has a known size that is the same as the + * remaining space in the buffer. in this case, we copy + * it into the buffer and flush the buffer immediately. + * + * - the ksraw has a known size that is longer than the + * remaining space in the buffer. in this case, we flush + * the buffer immediately and then copy in the new string. + * + * - the ksraw has a known size that is greater than or + * equal to the buffer's run. in this case, we flush the + * buffer and print the ksraw directly, bypassing the + * buffer as it would only be a performance impediment. + */ + + if (src.size == 0) src.size = kssz(src.ptr, -1); + + if (src.size < dest.size) { + kcond c = kscp(src, dest, null); + if (!kokay(c)) return c; + b -> cur += src.size; + return kscond_ok; + } else if (src.size == dest.size) { + kcond c = kscp(src, dest, null); + if (!kokay(c)) return c; + b -> cur += src.size; + return ksbufflush(b); + } else if (src.size > b -> run) { + kcond c = ksbufflush(b); + if (!kokay(c)) return c; + return kiosend(b -> channel, src, null); + } else if (src.size > dest.size) { + kcond c = ksbufflush(b); + if (!kokay(c)) return c; + dest.size = b -> run; + dest.ptr = b -> cur; + c = kscp(src, dest, null); + if (!kokay(c)) return c; + b -> cur += src.size; + return kscond_ok; + } else return kscond_fail; /* what in the sam heck */ +} ADDED mod/kstr/kssz.fn.c Index: mod/kstr/kssz.fn.c ================================================================== --- mod/kstr/kssz.fn.c +++ mod/kstr/kssz.fn.c @@ -0,0 +1,8 @@ +#include +#include + +sz kssz(const char* str, sz max) { + const char* end; + for (end = str; *end!=0; ++end); + return end - str; +} Index: mod/kstr/kstr.md ================================================================== --- mod/kstr/kstr.md +++ mod/kstr/kstr.md @@ -1,59 +1,70 @@ # kstr **kstr** is the libk string library. it uses the **short** naming convention with the glyph `s`. **kstr** implies `#include `. -## types +# types -### struct kstr +## struct kstr `struct kstr` is a structure for holding pascal strings (length-prefixed strings). it is the basic libk string type. **note:** if `ptr.ref` ≠ NULL and `sz` = 0, the string's length is unknown and should be calculated by any function that operates on a kstr, storing the result in the object if possible. * `sz size` - length of string, excluding any null terminator * `kmptr ptr` - pointer to string in memory -### struct ksraw +## struct ksraw `struct ksraw` is like `kstr` except it uses raw `char` pointers instead of a `kmptr`. * `sz size` - length of string, excluding any null terminator * `char* ptr` - pointer to string in memory -### struct ksbuf -`struct ksbuf` is a structure used to hold buffers. - * `sz size` - maximum size of buffer, including any null terminator - * `char* buf` - region of memory to store buffer in - * `ksalloc strat` - allocation strategy - * `kmkind rule` - kind of allocator to use. only needs to be set if `where` is NULL. see [kmem](../kmem/kmem.md). - * `kmcell* where` - where to allocate the object, in case of pool or tree allocation. +## struct ksbuf +`struct ksbuf` is a structure used for buffered IO. + * `sz run` - maximum size of buffer, including any null terminator + * `kiochan channel` - the channel that output will be written to when flushed + * `char* cur` - a pointer that tracks the length of the buffer + * `char buf []` - region of memory to store buffer in -### struct kschain +## struct kschain `struct kschain` is a structure used for string accumulators that works by aggregating pointers to strings, instead of copying the strings themselves. * `kschain_kind kind` - kind of chain * `kmkind rule` - kind of allocation to use if `kind` ≠ `kschain_kind_linked` * `pstr* ptrs` - pointer to pointer list * `sz ptrc` - number of pointers * `sz size` - total amount of space in `ptrs` -#### enum kschain_kind +### enum kschain_kind * `kschain_kind_block` - occupies a single block of memory * `kschain_kind_linked` - uses a linked list, allocated and deallocated as necessary -### enum ksalloc -`enum ksalloc` is an enumerator that tells libk what strategy to use when filling a `ksbuf` or `kschain` struct. +## enum ksalloc +`enum ksalloc` is an enumerator that tells libk what strategy to use when filling a `kschain` struct. * `ksalloc_static` - do not allocate memory, fill an already-allocated, statically-sized array. * `ksalloc_alloc` - allocate a string in memory using the specified kind of allocator. * `ksalloc_dynamic` - fill an already-allocated array if possible, allocate a string in memory if the string length exceeds available space. -## functions +# functions -### kssz +## kssz `size_t kssz(char* str, size_t max)` returns the number of characters in a C string, **including** the final null. will count at most `max` characters if `max` > 0. -### kstr +## kstr `kstr kstr(char* str, size_t max)` takes a C string and returns a P-string, calculating the length of `str` and storing it in the return value. `max` works as in `kssz`. -### kstoraw +## kstoraw `ksraw ksref(kstr)` is a simple convenience function that returns the `ksraw` form of a `kstr`. -### kscomp +## kscp +`kscond kscp(ksraw src, ksmut dest, sz* len)` copies the string pointed to by `src` into `dest`. its behavior varies depending on the value of `src.size` — if the size is already known, attempts to copy a longer string on top of a shorter one will immediately fail with no changes made to either string. if the size is set to zero, `kscp()` will copy as many bytes as it can before it hits either a NUL terminator in the source string or reaches the end of the destination string. if `dest.src` is zero, kscp simply copies until it hits the first NUL, or reaches `src.ptr[src.size - 1]`. for safety reasons, kscp always terminates `dest` with a NUL when it has enough space to, even if neither string ended with a NUL. if a partial copy occurs, `kscp` will return a `kscond` of `kscond_partial`. + +## ksbufmk +`ksbuf* ksbufmk(void* where, kiochan channel, sz run)` initializes a new buffer at the specified address. `run` should be equivalent to the full length of the memory region minus `sizeof(struct ksbuf)` - in other words, the size of the string the `ksbuf` can hold. memory should be allocated by the user, either by creating an array on the stack or with `kmem` allocation functions. `ksbufmk()` returns a pointer to the new structure. the return value will always point to the same point in memory as `where`, but will be of the appropriate type to pass to buffer functions. + +## ksbufput +`kcond ksbufput(ksbuf* b, ksraw str)` copies a string into a buffer with `kscp`. flushing it as necessary. + +# ksbufflush +`kcond ksbufflush(ksbuf* b)` flushes a buffer to its assigned channel, emptying it and readying it for another write cycle. a buffer should almost always be flushed before it goes out of scope or is deallocated. + +## kscomp `char* kscomp(size_t ct, ksraw struct[], kmbuf* buf)` is a **string composition** function. it serves as an efficient, generalized replacement for functions like `strcat` and `strdup`. to use kscomp, create an array of `kstr` and fill it with the strings you wish to concatenate. for example, to programmatically generate an HTML link tag, you might use the following code. char mem[512]; @@ -67,9 +78,9 @@ }; char* html = kscomp(Kmsz(chain), chain, &buf); kscomp will only calculate the length of individual strings if they are not already known. when it needs to calculate the length of a string, it will store that length in the original array so repeated calls can be made without needing to repeatedly calculate the lengths. this is not always desirable, so the variant `kscompc` exists, which is exactly the same as `kscomp` in every respect except that `chain` is not altered in any way. -### macros +## macros if `KFclean` is not set when is included, the following macros are defined. * `Kstr(string)` - the compile-time equivalent to `kstr()`. `Kstr` takes a literal string and inserts the text `{ sizeof (string), string }` into the document, suitable for initializing a kstr. Index: mod/kstr/str.h ================================================================== --- mod/kstr/str.h +++ mod/kstr/str.h @@ -1,32 +1,39 @@ #ifndef KIstr #define KIstr -#include +#include #ifdef __cplusplus extern "C" { #endif -typedef struct kstr { - sz size; - kmptr ptr; -} kstr; - typedef struct ksraw { sz size; const char* ptr; } ksraw; typedef struct ksmut { sz size; char* ptr; } ksmut; + +sz kssz(const char* str, sz max); + +#include + +typedef struct kstr { + sz size; + kmptr ptr; +} kstr; + +#include #include typedef enum kscond { kscond_ok = kscond_id, + kscond_partial, kscond_fail, kscond_unimplemented, kscond_nonnumeric, kscond_no_room, kscond_null, @@ -46,20 +53,38 @@ ksconv_nopfx = 1 << 8, ksconv_endh = 1 << 9, ksconv_endl = 1 << 10, }; + +typedef struct ksbuf { + sz run; + kiochan channel; + char* cur; + char buf []; +} ksbuf; + +/* functions */ + kscond ks_to_int(ksraw str, enum ksconv mode, u8* dest, sz size); kscond ks_of_int(u8* number, sz size, enum ksconv mode, char* bufstart, sz bufsize); kscond kscp(ksraw str, ksmut dest, sz* len); + +ksbuf* ksbufmk(void* where, kiochan channel, sz run); + +#include + +kcond ksbufput(ksbuf*, ksraw); + +kiocond ksbufflush(ksbuf*); #ifdef __cplusplus } #endif #endif