kerep sources

This commit is contained in:
2023-07-12 13:53:20 +03:00
parent 683395983c
commit 44ee9e210f
88 changed files with 3051 additions and 98 deletions

17
kerep/src/base/base.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
#include "std.h"
#include "errors.h"
#include "cptr.h"
#include "optime.h"
#include "type_system/type_system.h"
#include "../kprint/kprintf.h"
#include "endian.h"
#if __cplusplus
}
#endif

217
kerep/src/base/cptr.c Normal file
View File

@@ -0,0 +1,217 @@
#include "base.h"
#include "../String/StringBuilder.h"
#include <ctype.h>
// returns length of char buffer (without \0)
u32 cptr_length(const char* str){
const char *const str_first=str;
while(*str)
str++;
return str-str_first;
}
// allocates new char[] and copies src there
char* cptr_copy(const char* src){
u32 len=cptr_length(src)+1;
char* dst=malloc(len);
while(len--!=0)
dst[len]=src[len];
return dst;
}
// multiplies char n times
char* char_multiply(char c, u32 n){
char* rez=malloc(n+1);
rez[n]=0;
while(n--!=0)
rez[n]=c;
return rez;
}
bool cptr_equals(const char* key0, const char* key1){
char c0=*key0;
char c1=*key1;
bool eq=c0==c1;
while(c0 && c1 && eq) {
c0=*++key0;
c1=*++key1;
eq=c0==c1;
}
return eq;
}
bool cptr_startsWith(const char* src, const char* fragment){
char c0=*src;
char c1=*fragment;
bool eq=c0==c1 && c0 !=0 && c1!=0;
while(c0 && c1 && eq) {
c0=*++src;
c1=*++fragment;
eq=c0==c1;
if(c1==0)
return true;
}
return eq;
}
bool cptr_endsWith(const char* src, const char* fragment){
u32 src_len=cptr_length(src);
u32 fr_len=cptr_length(fragment);
if(src_len<fr_len || src_len==0 || fr_len==0)
return false;
src+=src_len-fr_len;
return cptr_equals(src, fragment);
}
i32 cptr_seek(const char* src, const char* fragment, u32 startIndex, u32 seekLength){
char sc=*src, fc=*fragment;
if(sc==0 || fc==0)
return -1;
u32 fr_start=startIndex;
for(u32 si=startIndex; si-startIndex<seekLength && sc!=0; si++){
sc=src[si];
fc=fragment[si-fr_start];
if(fc==0)
return fr_start;
if(sc!=fc)
fr_start++;
}
return -1;
}
i32 cptr_seekReverse(const char* src, const char* fragment, u32 startIndex, u32 seekLength){
char sc=*src, fc=*fragment;
if(sc==0 || fc==0)
return -1;
i32 len=cptr_length(src);
if(startIndex==(u32)-1)
startIndex=len-1;
u32 fr_len=cptr_length(fragment);
for(u32 si=startIndex; si<(u32)-1 && si!=len-1-seekLength; si--){
if(si+1<fr_len)
return -1;
sc=src[si];
fc=fragment[0];
u32 fr_start=si;
for(u32 fi=0; fc==sc ; fi++){
if(fi==fr_len)
return fr_start;
fc=fragment[fi];
sc=src[si--];
}
}
return -1;
}
i32 cptr_seekChar(const char* src, char fragment, u32 startIndex, u32 seekLength){
char sc=*src;
if(sc==0 || fragment==0)
return -1;
for(u32 si=startIndex; si-startIndex<seekLength && sc!=0; si++){
sc=src[si];
if(sc==fragment)
return si;
}
return -1;
}
i32 cptr_seekCharReverse(const char* src, char fragment, u32 startIndex, u32 seekLength){
char sc=*src;
if(sc==0 || fragment==0)
return -1;
i32 len=cptr_length(src);
if(startIndex==(u32)-1)
startIndex=len-1;
for(u32 si=startIndex; si<(u32)-1 && si!=len-1-seekLength; si--){
sc=src[si];
if(sc==fragment)
return si;
}
return -1;
}
void memcopy(void* from, void* to, u32 size){
if(from==NULL || to==NULL)
throw(ERR_NULLPTR);
for(u32 i=0; i<size; i++)
((char*)to)[i]=((char*)from)[i];
}
char* __cptr_concat(u32 n, ...){
char** strs=(char**)malloc(n*sizeof(char*));
u32* lengths=malloc(n*sizeof(u32));
u32 totalLength=0;
// reading args from va_list
va_list vl;
va_start(vl, n);
for(u16 i=0; i<n; i++){
char* str=va_arg(vl,char*);
i16 length=cptr_length(str);
strs[i]=str;
lengths[i]=length;
totalLength+=length;
}
va_end(vl);
// allocating memory for output value
char* totality=malloc(totalLength+1);
char* output=totality;
totality[totalLength]=0;
// copying content of all strings to rezult
for(u16 k=0; k<n; k++){
memcopy(strs[k], totality, lengths[k]);
totality+=lengths[k];
}
free(strs);
free(lengths);
return output;
}
char* cptr_toLower(const char* src) {
u32 length=cptr_length(src);
char *p=malloc(length+1);
p[length]=0;
for(u32 i=0; i<length; i++)
p[i]=tolower(src[i]);
return p;
}
char* cptr_toUpper(const char* src) {
u32 length=cptr_length(src);
char *p=malloc(length+1);
p[length]=0;
for(u32 i=0; i<length; i++)
p[i]=toupper(src[i]);
return p;
}
char* cptr_replaceCharIn(const char* src, char c_old, char c_new, u32 startIndex, u32 seekLength){
char* rzlt=cptr_copy(src);
for(u32 i=startIndex; i!=seekLength && src[i]!=0; i++){
if(src[i]==c_old)
rzlt[i]=c_new;
}
return rzlt;
}
char* cptr_replaceIn(const char* src, const char* str_old, const char* str_new, u32 startIndex, u32 seekLength){
StringBuilder* sb=StringBuilder_create();
const u32 str_old_len=cptr_length(str_old);
const u32 str_new_len=cptr_length(str_new);
i32 i=startIndex;
while( (i=cptr_seek(src, str_old, startIndex, seekLength)) !=-1 ){
if(i!=0)
StringBuilder_append_string(sb, (string){.ptr=(char*)src, .length=i});
StringBuilder_append_string(sb, (string){.ptr=str_new, .length=str_new_len});
src+=i+str_old_len;
}
u32 src_remains_len=cptr_length(src);
if(src_remains_len>0)
StringBuilder_append_string(sb, (string){.ptr=(char*)src, .length=src_remains_len});
string rezult=StringBuilder_build(sb);
return rezult.ptr;
}

94
kerep/src/base/cptr.h Normal file
View File

@@ -0,0 +1,94 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
#include "std.h"
// returns length of char buffer (without \0)
u32 cptr_length(const char* str);
// allocates new char[] and copies src there
char* cptr_copy(const char* src);
bool cptr_equals(const char* key0, const char* key1);
bool cptr_startsWith(const char* src, const char* fragment);
bool cptr_endsWith(const char* src, const char* fragment);
// multiplies char n times
char* char_multiply(char c, u32 n);
/// @param startIndex 0 ... src length
/// @param seekLength 0 ... -1
/// @return pos of first <fragment> inclusion in <src> or -1 if not found
i32 cptr_seek(const char* src, const char* fragment, u32 startIndex, u32 seekLength);
/// @param startIndex -1 ... src length
/// @param seekLength 0 ... -1
/// @return pos of first <fragment> inclusion in <src> or -1 if not found
i32 cptr_seekReverse(const char* src, const char* fragment, u32 startIndex, u32 seekLength);
/// @param startIndex 0 ... src length
/// @param seekLength 0 ... -1
/// @return pos of first <fragment> inclusion in <src> or -1 if not found
i32 cptr_seekChar(const char* src, char fragment, u32 startIndex, u32 seekLength);
/// @param startIndex -1 ... src length
/// @param seekLength 0 ... -1
/// @return pos of first <fragment> inclusion in <src> or -1 if not found
i32 cptr_seekCharReverse(const char* src, char fragment, u32 startIndex, u32 seekLength);
/// @brief search for <fragment> in <ptr>
/// @return index of first <fragment> inclusion or -1 if not found
static inline i32 cptr_indexOf(const char* src, const char* fragment)
{ return cptr_seek(src, fragment, 0, -1); }
/// @brief search for <fragment> in <ptr>
/// @return index of first <fragment> inclusion or -1 if not found
static inline i32 cptr_indexOfChar(const char* src, char fragment)
{ return cptr_seekChar(src, fragment, 0, -1); }
/// @brief search for <fragment> in <ptr>
/// @return index of last <fragment> inclusion or -1 if not found
static inline i32 cptr_lastIndexOf(const char* src, const char* fragment)
{ return cptr_seekReverse(src, fragment, -1, -1); }
/// @brief search for <fragment> in <ptr>
/// @return index of last <fragment> inclusion or -1 if not found
static inline i32 cptr_lastIndexOfChar(const char* src, char fragment)
{ return cptr_seekCharReverse(src, fragment, -1, -1); }
static inline bool cptr_contains(const char* src, const char* fragment){
return cptr_seek(src, fragment, 0, -1) +1;
}
void memcopy(void* from, void* to, u32 size);
char* __cptr_concat(u32 n, ...);
#define cptr_concat(STR...) __cptr_concat(count_args(STR), STR)
char* cptr_toLower(const char* src);
char* cptr_toUpper(const char* src);
/// @param startIndex 0 ... src length
/// @param seekLength 0 ... -1
/// @return <src> with <str_old> replaced by <str_new> or empty cstring if <str_old> not found
char* cptr_replaceIn(const char* src, const char* str_old, const char* str_new, u32 startIndex, u32 seekLength);
/// @param startIndex 0 ... src length
/// @param seekLength 0 ... -1
/// @return <src> with <c_old> replaced by <c_new> or empty cstring if <str_old> not found
char* cptr_replaceCharIn(const char* src, char c_old, char c_new, u32 startIndex, u32 seekLength);
static inline char* cptr_replace(const char* src, const char* str_old, const char* str_new)
{ return cptr_replaceIn(src, str_old, str_new, 0, -1); }
static inline char* cptr_replaceChar(const char* src, char c_old, char c_new)
{ return cptr_replaceCharIn(src, c_old, c_new, 0, -1); }
#if __cplusplus
}
#endif

13
kerep/src/base/endian.c Normal file
View File

@@ -0,0 +1,13 @@
#include "endian.h"
static const union
{
u16 number;
Endian bytes[2];
} _endian_union={ .number=0x0102 };
Endian getEndian(){
// if 0x0102 == { 1, 2 } then BigEndian
// if 0x0102 == { 2, 1 } then LittleEndian
return _endian_union.bytes[1];
}

20
kerep/src/base/endian.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
#include "std.h"
#include "type_system/typedef_macros.h"
PACKED_ENUM(Endian,
UnknownEndian=0,
LittleEndian=1,
BigEndian=2
)
Endian getEndian();
#if __cplusplus
}
#endif

59
kerep/src/base/errors.c Normal file
View File

@@ -0,0 +1,59 @@
#include "std.h"
#include "errors.h"
#include "cptr.h"
#include "../kprint/kprintf.h"
char* errname(ErrorId err){
switch(err){
case SUCCESS: return nameof(SUCCESS);
case ERR_MAXLENGTH: return nameof(ERR_MAXLENGTH);
case ERR_WRONGTYPE: return nameof(ERR_WRONGTYPE);
case ERR_WRONGINDEX: return nameof(ERR_WRONGINDEX);
case ERR_NOTIMPLEMENTED: return nameof(ERR_NOTIMPLEMENTED);
case ERR_NULLPTR: return nameof(ERR_NULLPTR);
case ERR_ENDOFSTR: return nameof(ERR_ENDOFSTR);
case ERR_KEYNOTFOUND: return nameof(ERR_KEYNOTFOUND);
case ERR_FORMAT: return nameof(ERR_FORMAT);
case ERR_UNEXPECTEDVAL: return nameof(ERR_UNEXPECTEDVAL);
case ERR_IO: return nameof(ERR_IO);
case ERR_IO_EOF: return nameof(ERR_IO_EOF);
default: return "UNKNOWN_ERROR";
}
}
#define ERRMSG_MAXLENGTH 1024
char* __genErrMsg(const char* errmsg, const char* srcfile, i32 line, const char* funcname){
size_t bufsize=ERRMSG_MAXLENGTH;
char* rezult=malloc(bufsize);
IFMSC(
sprintf_s(rezult,bufsize,"[%s:%d] %s() throwed error: %s",srcfile,line,funcname,errmsg),
sprintf(rezult,"[%s:%d] %s() throwed error: %s",srcfile,line,funcname,errmsg)
);
return rezult;
}
char* __extendErrMsg(const char* errmsg, const char* srcfile, i32 line, const char* funcname){
size_t bufsize=cptr_length(errmsg)+ERRMSG_MAXLENGTH;
char* rezult=malloc(bufsize);
IFMSC(
sprintf_s(rezult,bufsize,"%s\n \\___[%s:%d] %s()",errmsg,srcfile,line,funcname),
sprintf(rezult,"%s\n \\___[%s:%d] %s()",errmsg,srcfile,line,funcname)
);
free(errmsg);
return rezult;
}
void Maybe_free(Maybe e){
free(e.errmsg);
Unitype_free(e.value);
}
void printMaybe(Maybe e){
if(e.errmsg) kprintf("%s\n",e.errmsg);
else printuni(e.value);
}
char* __doNothing(char* a) {return a;}
char* __unknownErr() {return "UNKNOWN ERROR";}

90
kerep/src/base/errors.h Normal file
View File

@@ -0,0 +1,90 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
#include "std.h"
#include "type_system/type_system.h"
PACKED_ENUM(ErrorId,
SUCCESS, // not an error
ERR_MAXLENGTH, ERR_WRONGTYPE, ERR_WRONGINDEX,
ERR_NOTIMPLEMENTED, ERR_NULLPTR, ERR_ENDOFSTR,
ERR_KEYNOTFOUND, ERR_FORMAT, ERR_UNEXPECTEDVAL,
ERR_IO, ERR_IO_EOF
)
char* errname(ErrorId err);
char* __genErrMsg(const char* errmsg, const char* srcfile, i32 line, const char* funcname);
char* __extendErrMsg(const char* errmsg, const char* srcfile, i32 line, const char* funcname);
STRUCT(Maybe,
Unitype value;
char* errmsg;
)
// return it if func doesn't return anything
// .value .errmsg
#define MaybeNull (Maybe){UniNull, NULL}
void Maybe_free(Maybe e);
void printMaybe(Maybe e);
#define SUCCESS(REZLT) (Maybe){.errmsg=NULL, .value=REZLT}
#define __RETURN_EXCEPTION(ERRMSG) return (Maybe){.value=UniNull, .errmsg=ERRMSG}
#define __EXIT(ERRMSG) ({ kprintf("\e[91m%s\e[0m \n", ERRMSG); free(ERRMSG); exit(128); })
char* __doNothing(char* a);
char* __unknownErr( );
#define __stringify_err(E) _Generic( \
(E), \
char*: __doNothing, \
int: errname, \
default: __unknownErr \
)(E)
#if __cplusplus
#define throw_id(E) __EXIT(((char*)__genErrMsg(errname(E), __FILE__,__LINE__,__func__)))
#define throw_msg(E) __EXIT(((char*)__genErrMsg(E, __FILE__,__LINE__,__func__)))
#define safethrow_id(E, FREEMEM) { FREEMEM; \
__RETURN_EXCEPTION(((char*)__genErrMsg(errname(E), __FILE__,__LINE__,__func__))); \
}
#define safethrow_msg(E, FREEMEM) { FREEMEM; \
__RETURN_EXCEPTION(((char*)__genErrMsg(E, __FILE__,__LINE__,__func__))); \
}
#define try_cpp(_funcCall, _rezult, freeMem) Maybe _rezult=_funcCall; if(_rezult.errmsg){ \
freeMem; \
_rezult.errmsg=__extendErrMsg(_rezult.errmsg, __FILE__,__LINE__,__func__); \
return _rezult; \
}
#else
#define throw(E) __EXIT(((char*)__genErrMsg((__stringify_err(E)), __FILE__,__LINE__,__func__)))
#define safethrow(E, FREEMEM) { FREEMEM; \
__RETURN_EXCEPTION(((char*)__genErrMsg((__stringify_err(E)), __FILE__,__LINE__,__func__))); \
}
#define try(_funcCall, _rezult, freeMem) Maybe _rezult=_funcCall; if(_rezult.errmsg){ \
freeMem; \
_rezult.errmsg=__extendErrMsg(_rezult.errmsg, __FILE__,__LINE__,__func__); \
return _rezult; \
}
#endif
#define tryLast(_funcCall, _rezult, ON_EXIT) Maybe _rezult=_funcCall; if(_rezult.errmsg){ \
_rezult.errmsg=__extendErrMsg(_rezult.errmsg, __FILE__,__LINE__,__func__); \
__EXIT(_rezult.errmsg); \
}
#if __cplusplus
}
#endif

41
kerep/src/base/optime.h Normal file
View File

@@ -0,0 +1,41 @@
#pragma once
#include "std.h"
#define __optime_print(opname, t) \
char tnames[3][3]={"s\0","ms","us"}; \
i32 tni=0; \
if(t>1000000){ \
t/=1000000; \
tni=0; \
} else if(t>1000){ \
t/=1000; \
tni=1; \
} else tni=2; \
kprintf("\e[93moperation \e[94m%s\e[93m lasted \e[94m%f \e[93m%s\n", \
opname, t, tnames[tni]);
#ifdef CLOCK_REALTIME
/// executes codeblock and prints execution time
/// u64 op_i is counter of the internal loop
/// uses non-standard high-precision clock
#define optime(opname, repeats, codeblock...) { \
struct timespec start, stop; \
clock_gettime(CLOCK_REALTIME, &start); \
for(u64 op_i=0;op_i<(u64)repeats;op_i++) \
{ codeblock; } \
clock_gettime(CLOCK_REALTIME, &stop); \
f64 t=(f64)(stop.tv_sec-start.tv_sec)*1000000+(f64)(stop.tv_nsec-start.tv_nsec)/1000; \
__optime_print(opname,t); \
}
#else
/// uses standard low precision clock
#define optime(opname, repeats, codeblock...) { \
clock_t start=clock(); \
for(u64 op_i=0;op_i<(u64)repeats;op_i++) \
{ codeblock; } \
clock_t stop=clock(); \
f64 t=(f64)(stop-start)/CLOCKS_PER_SEC*1000000; \
__optime_print(opname,t); \
}
#endif

139
kerep/src/base/std.h Normal file
View File

@@ -0,0 +1,139 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
#include <locale.h>
#include <time.h>
#include <setjmp.h>
#include <signal.h>
#include <math.h>
typedef int8_t i8;
typedef uint8_t u8;
typedef int16_t i16;
typedef uint16_t u16;
typedef int32_t i32;
typedef uint32_t u32;
typedef int64_t i64;
typedef uint64_t u64;
typedef float f32;
typedef double f64;
/// anonymous pointer without specified freeMembers() func
typedef void* Pointer;
// Usually bool from stdbool.h is defined as macro,
// so in other macros like ktid_##TYPE it will be replaced by _Bool.
// ktid__Bool will be created instead of ktid_bool
// In C++ bool is a keyword, so there is no need to redefine it.
#if !__cplusplus
typedef u8 bool;
#define true 1
#define false 0
#endif
#ifndef typeof
#define typeof __typeof__
#endif
#define dbg(N) kprintf("\e[95m%d\n",N)
#define nameof(V) #V
#ifdef _MSC_VER
#pragma comment(lib, "mincore_downlevel.lib") // Support OS older than SDK
#define _CRT_SECURE_NO_WARNINGS 1
#define EXPORT __declspec(dllexport)
#define CALL __cdecl
#elif defined(__GNUC__)
#define EXPORT __attribute__((visibility("default")))
#if __SIZEOF_POINTER__ == 4
#define CALL __attribute__((__cdecl__))
#else
#define CALL
#endif
#ifndef typeof
#define typeof(X) __typeof__(X)
#endif
#else
#pragma GCC error "unknown compiler"
#endif
#ifdef _MSC_VER
#define IFWIN(YES, NO) YES
#define IFMSC(YES, NO) YES
#elif defined(_WIN64) || defined(_WIN32)
#define IFWIN(YES, NO) YES
#define IFMSC(YES, NO) NO
#elif defined(__GNUC__)
#define IFWIN(YES, NO) NO
#define IFMSC(YES, NO) NO
#else
#pragma GCC error "unknown compiler"
#endif
#ifndef sprintf_s
#define sprintf_s(BUF, BUFSIZE, FORMAT, ...) sprintf(BUF, FORMAT, ## __VA_ARGS__)
#endif
#define __count_args( \
a0, a1, a2, a3, a4, a5, a6, a7 , \
a8, a9, a10,a11,a12,a13,a14,a15, \
a16,a17,a18,a19,a20,a21,a22,a23, \
a24,a25,a26,a27,a28,a29,a30,a31, \
a32,a33,a34,a35,a36,a37,a38,a39, \
a40,a41,a42,a43,a44,a45,a46,a47, \
a48,a49,a50,a51,a52,a53,a54,a55, \
a56,a57,a58,a59,a60,a61,a62,a63, \
a64,...) a64
// Macro for counting variadic arguments (max 64)
// (see usage in kprint.h)
#define count_args(ARGS...) __count_args( \
ARGS, \
64,63,62,61,60,59,58,57, \
56,55,54,53,52,51,50,49, \
48,47,46,45,44,43,42,41, \
40,39,38,37,36,35,34,33, \
32,31,30,29,28,27,26,25, \
24,23,22,21,20,19,18,17, \
16,15,14,13,12,11,10,9, \
8, 7, 6, 5, 4, 3, 2, 1, 0)
/*
Cross-platform warning supression.
WARNING_DISABLE( W_EXAMPLE,
some code producing W_EXAMPLE;
);
You can even embed it into macro in header (see kprint.h)
*/
#ifdef _MSC_VER
#define PRAGMA_WARNING_PUSH __pragma(warning( push ))
#define DISABLE_WARNING(wNumber) __pragma(warning( disable : wNumber ))
#define PRAGMA_WARNING_POP __pragma(warning( pop ))
#else
#define _PRAGMA(P) _Pragma(#P)
#define PRAGMA_WARNING_PUSH _PRAGMA(GCC diagnostic push)
#define PRAGMA_WARNING_DISABLE(wName) _PRAGMA(GCC diagnostic ignored wName)
#define PRAGMA_WARNING_POP _PRAGMA(GCC diagnostic pop)
#define W_INT_CONVERSION "-Wint-conversion"
#define W_IMPLICIT_FALLTHROUGH "-Wimplicit-fallthrough"
#endif
#define WARNING_DISABLE(WARNING, CODE...) \
PRAGMA_WARNING_PUSH \
PRAGMA_WARNING_DISABLE(WARNING) \
CODE; \
PRAGMA_WARNING_POP
/// gcc throws warning on unused function return value
#define WARN_UNUSED_REZULT __attribute__((warn_unused_result))
#if __cplusplus
}
#endif

View File

@@ -0,0 +1,34 @@
# kerep type system
For using some kerep capabilities, such as generic structs, unitype, and kprint, types should be *registered*.
## type id
Every registered type has its own `ktDescriptor` and `ktid` is an index of the descriptor in descriptors array.
Descriptor should be declared in header file.
Following macro declares `typedef struct` and `ktDescriptor`
```c
//someStruct.h
STRUCT(someStruct,
i32 i; i32 j; i32 k;
);
```
then you need to define descriptor in a source file
```c
//someStruct.c
kt_define(someStruct);
```
and register it.
## type descriptors
Every registered type should have it's own descriptor (`ktDescriptor`). It's a struct, which contains some information about type and pointers to some specific functions for this type (`toString`, `freeMembers`).
## type registration
To finally register a type, you should call macro `kt_register()` between `kt_beginInit()` and `kt_endInit()`. Better do it at the start of your program. To register all types from kerep, call `kt_initKerepTypes()`.
You can free internal ktDescriptors storage by calling `kt_free()` at exit, if your debugger (valgrind in my case) sees a memory leak.
Examples:
+ [kerep types registration](src/base/type_system/init.c)
+ [kt_initKerepTypes()](tests/main.cpp)

View File

@@ -0,0 +1,238 @@
#include "base_toString.h"
#include "../base.h"
#include "../../kprint/kprint_format.h"
// accepts char* (ptr to char) and char* (ptr to string)
// uses format kp_s and kp_c to determine what type is <c> argument
char* __toString_char(void* c, u32 fmt) {
// *c=char*
if(kp_fmt_dataFormat(fmt)==kp_s){
return cptr_copy((char*)c); // to avoid segmentation fault on free() when *c allocalet on stack
}
// *c=char
if(kp_fmt_dataFormat(fmt)==kp_c){
char* cc=malloc(2);
cc[0]=*(char*)c;
cc[1]=0;
return cc;
}
else throw(ERR_FORMAT);
}
char* __toString_bool(void* c, u32 fmt) {
static const char _strbool[4][6]={ "false", "true\0", "False", "True\0" };
u8 strind=*(bool*)c==1 + kp_fmt_isUpper(fmt)*2;
char* rez=malloc(6);
rez[0]=_strbool[strind][0];
rez[1]=_strbool[strind][1];
rez[2]=_strbool[strind][2];
rez[3]=_strbool[strind][3];
rez[4]=_strbool[strind][4];
rez[5]=0;
return rez;
}
char* toString_i64(i64 n){
i64 d=n<0 ? -1*n : n;
char str[32];
u8 i=sizeof(str);
str[--i]=0;
if(d==0)
str[--i]='0';
else while(d!=0){
str[--i]='0' + d%10;
d/=10;
}
if(n<0)
str[--i]='-';
return cptr_copy((char*)str+i);
}
char* toString_u64(u64 n, bool withPostfix, bool uppercase){
char str[32];
u8 i=sizeof(str);
str[--i]=0;
if(withPostfix)
str[--i]= uppercase ? 'U' : 'u';
if(n==0)
str[--i]='0';
else while(n!=0){
str[--i]='0' + n%10;
n/=10;
}
return cptr_copy((char*)str+i);
}
#define _toString_float_impl(bufsize, maxPrecision) { \
char str[bufsize]; \
if(precision>maxPrecision) \
throw("too big precision"); \
if(precision==0) \
precision=toString_float_default_precision; \
i32 cn=IFMSC( \
sprintf_s(str, bufsize, "%.*f", precision, n), \
sprintf(str, "%.*f", precision, n) \
); \
/* remove trailing zeroes except .0*/ \
while(str[cn-1]=='0' && str[cn-2]!='.') \
cn--; \
if(withPostfix) \
str[cn++]= uppercase ? 'F' : 'f'; \
str[cn]='\0'; \
return cptr_copy(str); \
}
char* toString_f32(f32 n, u8 precision, bool withPostfix, bool uppercase)
_toString_float_impl(48, toString_f32_max_precision)
char* toString_f64(f64 n, u8 precision, bool withPostfix, bool uppercase)
_toString_float_impl(512, toString_f64_max_precision)
#define byte_to_bits(byte) { \
str[cn++]='0' + (u8)((byte>>7)&1); /* 8th bit */ \
str[cn++]='0' + (u8)((byte>>6)&1); /* 7th bit */ \
str[cn++]='0' + (u8)((byte>>5)&1); /* 6th bit */ \
str[cn++]='0' + (u8)((byte>>4)&1); /* 5th bit */ \
str[cn++]='0' + (u8)((byte>>3)&1); /* 4th bit */ \
str[cn++]='0' + (u8)((byte>>2)&1); /* 3th bit */ \
str[cn++]='0' + (u8)((byte>>1)&1); /* 2th bit */ \
str[cn++]='0' + (u8)((byte>>0)&1); /* 1th bit */ \
}
char* toString_bin(void* _bytes, u32 size, bool inverse, bool withPrefix){
char* bytes=_bytes;
char* str=malloc(size*8 + (withPrefix?2:0) +1);
u32 cn=0; // char number
if(withPrefix){
str[cn++]='0';
str[cn++]='b';
}
if(inverse){
// byte number
for(i32 bn=size-1; bn>=0; bn--)
byte_to_bits(bytes[bn])
} else {
for(u32 bn=0; bn<size; bn++)
byte_to_bits(bytes[bn])
}
str[cn]=0;
return str;
}
// converts number from 0 to F to char
char _4bitsHex(u8 u, bool uppercase){
switch(u){
case 0: case 1: case 2: case 3: case 4:
case 5: case 6: case 7: case 8: case 9:
return '0'+u;
case 0xA: case 0xB: case 0xC:
case 0xD: case 0xE: case 0xF:
return (uppercase ? 'A' : 'a') + u -10;
default:
dbg(u);
throw("incorrect number");
return 219;
}
}
char* toString_hex(void* _bytes, u32 size, bool inverse, bool withPrefix, bool uppercase){
char* bytes=_bytes;
char* str=malloc(size*2 + (withPrefix?2:0) + 1);
u32 cn=0; // char number
if(withPrefix){
str[cn++]='0';
str[cn++]='x';
}
// left to right
if(inverse){
// byte number
for(i32 bn=size-1; bn>=0; bn--){
unsigned char byte=bytes[bn];
str[cn++]=_4bitsHex(byte/16, uppercase);
str[cn++]=_4bitsHex(byte%16, uppercase);
}
}
// right to left
else {
for(u32 bn=0; bn<size; bn++){ // byte number
unsigned char byte=bytes[bn];
str[cn++]=_4bitsHex(byte/16, uppercase);
str[cn++]=_4bitsHex(byte%16, uppercase);
}
}
str[cn]=0;
return str;
}
#define __toString_i32_def(BITS) char* __toString_i##BITS(void* _n, u32 f){ \
switch(kp_fmt_dataFormat(f)){ \
case kp_i: ; \
i##BITS n=*(i##BITS*)_n; \
return toString_i64(n); \
case kp_b: \
return toString_bin(_n, BITS/8, getEndian()==LittleEndian, kp_fmt_withPrefix(f)); \
case kp_h: \
return toString_hex(_n, BITS/8, getEndian()==LittleEndian, kp_fmt_withPrefix(f), kp_fmt_isUpper(f)); \
default: \
kprintf("\n%u\n", kp_fmt_dataFormat(f)); \
throw(ERR_FORMAT); \
return NULL; \
} \
}
__toString_i32_def(8)
__toString_i32_def(16)
__toString_i32_def(32)
__toString_i32_def(64)
#define __toString_u_def(BITS) char* __toString_u##BITS(void* _n, u32 f){ \
switch(kp_fmt_dataFormat(f)){ \
case kp_u: ; \
u##BITS n=*(u##BITS*)_n; \
return toString_u64(n, kp_fmt_withPostfix(f), kp_fmt_isUpper(f)); \
case kp_b: \
return toString_bin(_n, BITS/8, getEndian()==LittleEndian, kp_fmt_withPrefix(f)); \
case kp_h: \
return toString_hex(_n, BITS/8, getEndian()==LittleEndian, kp_fmt_withPrefix(f), kp_fmt_isUpper(f)); \
default: \
kprintf("\n%u\n", kp_fmt_dataFormat(f)); \
throw(ERR_FORMAT); \
return NULL; \
} \
}
__toString_u_def(8)
__toString_u_def(16)
__toString_u_def(32)
// __toString_u_def(64)
char* __toString_u64(void* _n, u32 f){
switch(kp_fmt_dataFormat(f)){
case kp_u: ;
u64 n=*(u64*)_n;
return toString_u64(n, kp_fmt_withPostfix(f), kp_fmt_isUpper(f));
case kp_b:
return toString_bin(_n, 64/8, getEndian()==LittleEndian, kp_fmt_withPrefix(f));
case kp_h:
return toString_hex(_n, 64/8, getEndian()==LittleEndian, kp_fmt_withPrefix(f), kp_fmt_isUpper(f));
default:
kprintf("\n%u\n", kp_fmt_dataFormat(f)); throw(ERR_FORMAT); return NULL; }
}
#define __toString_float_def(BITS) char* __toString_f##BITS(void* _n, u32 f){ \
switch(kp_fmt_dataFormat(f)){ \
case kp_f: ; \
f##BITS n=*(f##BITS*)_n; \
return toString_f64(n, toString_float_default_precision, kp_fmt_withPostfix(f), kp_fmt_isUpper(f)); \
case kp_b: \
return toString_bin(_n, BITS/8, getEndian()==LittleEndian, kp_fmt_withPrefix(f)); \
case kp_h: \
return toString_hex(_n, BITS/8, getEndian()==LittleEndian, kp_fmt_withPrefix(f), kp_fmt_isUpper(f)); \
default: \
kprintf("\n%u\n", kp_fmt_dataFormat(f)); \
throw(ERR_FORMAT); \
return NULL; \
} \
}
__toString_float_def(32)
__toString_float_def(64)

View File

@@ -0,0 +1,47 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
#include "../errors.h"
// accepts char* (ptr to char) and char* (ptr to string)
// uses format kp_s and kp_c to determine what type is <c> argument
char* __toString_char(void* c, u32 fmt);
// bool
char* __toString_bool(void* c, u32 fmt);
// signed int
char* toString_i64(i64 n);
char* __toString_i8(void* n, u32 fmt);
char* __toString_i16(void* n, u32 fmt);
char* __toString_i32(void* n, u32 fmt);
char* __toString_i64(void* n, u32 fmt);
// unsigned int
char* toString_u64(u64 n, bool withPostfix, bool uppercase);
char* __toString_u8(void* n, u32 fmt);
char* __toString_u16(void* n, u32 fmt);
char* __toString_u32(void* n, u32 fmt);
char* __toString_u64(void* n, u32 fmt);
// float
#define toString_f32_max_precision 6
#define toString_f64_max_precision 15
#define toString_float_default_precision 6
char* toString_f32(f32 n, u8 precision, bool withPostfix, bool uppercase); // uses sprintf
char* toString_f64(f64 n, u8 precision, bool withPostfix, bool uppercase); // uses sprintf
char* __toString_f32(void* n, u32 fmt);
char* __toString_f64(void* n, u32 fmt);
///@param inverse set to true for little endian numbers (their bytes are in reverse order)
char* toString_bin(void* bytes, u32 size, bool inverse, bool withPrefix);
///@param inverse set to true for little endian numbers (their bytes are in reverse order)
char* toString_hex(void* bytes, u32 size, bool inverse, bool withPrefix, bool uppercase);
#if __cplusplus
}
#endif

View File

@@ -0,0 +1,65 @@
#include "../base.h"
#include "../../Autoarr/Autoarr.h"
#include "../../Hashtable/Hashtable.h"
#include "../../String/StringBuilder.h"
#include "../../Filesystem/filesystem.h"
#include "base_toString.h"
void kt_initKerepTypes(){
// base types
kt_register(Pointer);
if(ktid_Pointer!=0) // this can break UnitypeNull
throw("ktid_Pointer!=0, you must init kerep types before any other types");
kt_register(char);
kt_register(bool);
kt_register(f32);
kt_register(f64);
kt_register(i8);
kt_register(u8);
kt_register(i16);
kt_register(u16);
kt_register(i32);
kt_register(u32);
kt_register(i64);
kt_register(u64);
// ktDescriptor
kt_register(ktDescriptor);
// base type autoarrs
kt_register(Autoarr_Pointer);
kt_register(Autoarr_char);
kt_register(Autoarr_bool);
kt_register(Autoarr_f32);
kt_register(Autoarr_f64);
kt_register(Autoarr_i8);
kt_register(Autoarr_u8);
kt_register(Autoarr_i16);
kt_register(Autoarr_u16);
kt_register(Autoarr_i32);
kt_register(Autoarr_u32);
kt_register(Autoarr_i64);
kt_register(Autoarr_u64);
// Unitype
kt_register(Unitype);
kt_register(Autoarr_Unitype);
// KeyValuePair
kt_register(KVPair);
kt_register(Autoarr_KVPair);
// Hashtable
kt_register(Hashtable);
// string
kt_register(string);
kt_register(Autoarr_string);
// StringBuilder
kt_register(StringBuilder);
//File
kt_register(FileHandle);
}

View File

@@ -0,0 +1,12 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
// call this between kt_beginInit() and kt_endInit()
void kt_initKerepTypes();
#if __cplusplus
}
#endif

View File

@@ -0,0 +1,51 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
#include "../std.h"
#include "ktid.h"
#include "typedef_macros.h"
#define kt_declare(TYPE)\
ktid_declare(TYPE);\
extern ktDescriptor ktDescriptor_##TYPE; \
extern ktDescriptor ktDescriptor_##TYPE##_Ptr;
#define kt_define(TYPE, FREE_MEMBERS_F, TOSTRING_F)\
ktid_define(TYPE); \
ktDescriptor ktDescriptor_##TYPE={ \
.name=#TYPE, \
.id=ktid_undefined, \
.size=sizeof(TYPE), \
.freeMembers=FREE_MEMBERS_F, \
.toString=TOSTRING_F \
}; \
ktDescriptor ktDescriptor_##TYPE##_Ptr={\
.name=#TYPE "_Ptr", \
.id=ktid_undefined, \
.size=sizeof(TYPE), \
.freeMembers=FREE_MEMBERS_F, \
.toString=TOSTRING_F \
};
typedef void (*freeMembers_t)(void*);
typedef char* (*toString_t)(void* obj, u32 fmt);
STRUCT(ktDescriptor,
char* name;
ktid id;
u16 size;
freeMembers_t freeMembers; // NULL or function which frees all struct members
toString_t toString; // NULL or function which generates string representaion of object
)
/// gets descriptor for TYPE
#define ktDescriptor_name(TYPE) ktDescriptor_##TYPE
/// gets descriptor for pointer to TYPE
#define ktDescriptor_namePtr(TYPE) ktDescriptor_##TYPE##_Ptr
#if __cplusplus
}
#endif

View File

@@ -0,0 +1,90 @@
#include "../../Autoarr/Autoarr.h"
#include "type_system.h"
#include "base_toString.h"
kt_define(Pointer, NULL, __toString_u64);
kt_define(char,NULL, __toString_char);
kt_define(bool,NULL, __toString_bool);
kt_define(f32, NULL, __toString_f32);
kt_define(f64, NULL, __toString_f64);
kt_define(i8, NULL, __toString_i8);
kt_define(u8, NULL, __toString_u8);
kt_define(i16, NULL, __toString_i16);
kt_define(u16, NULL, __toString_u16);
kt_define(i32, NULL, __toString_i32);
kt_define(u32, NULL, __toString_u32);
kt_define(i64, NULL, __toString_i64);
kt_define(u64, NULL, __toString_u64);
char* ktDescriptor_toString(ktDescriptor* d){
const char* n="null";
char *s0 = toString_u64(d->id, 0,0);
char *s1 = toString_u64(d->size, 0,0);
char *s2 = d->toString ? toString_hex(d->toString, sizeof(void*), 0,1,0) : n;
char *s3 = d->freeMembers ? toString_hex(d->freeMembers, sizeof(void*), 0,1,0) : n;
char *rez=cptr_concat("ktDescriptor {"
" name:", d->name,
" id:",s0,
" size:",s1,
" toString:",s2,
" freeMembers:",s3,
" }");
free(s0);
free(s1);
if(s2!=n) free(s2);
if(s3!=n) free(s3);
return rez;
}
char* _ktDescriptor_toString(void* _d, u32 fmt) { return ktDescriptor_toString(_d); }
kt_define(ktDescriptor, NULL, _ktDescriptor_toString);
typedef ktDescriptor* ktDescriptor_Ptr;
// type descriptors are stored here during initialization
Autoarr(Pointer)* __descriptorPointers=NULL;
// here type descriptors are stored when initialization is complited
ktDescriptor** typeDescriptors=NULL;
ktid ktid_last=-1;
ENUM(ktDescriptorsState,
NotInitialized, Initializing, Initialized
)
ktDescriptorsState initState=NotInitialized;
void kt_beginInit(){
#if DEBUG
kprintf("\e[94mtype descriptors initializing...\n");
#endif
__descriptorPointers=Autoarr_create(Pointer, 256, 256);
}
void kt_endInit(){
if(__descriptorPointers==NULL)
throw(ERR_NULLPTR);
typeDescriptors=(ktDescriptor**)Autoarr_toArray(__descriptorPointers);
Autoarr_free(__descriptorPointers,true);
if(typeDescriptors==NULL) throw(ERR_NULLPTR);
#if DEBUG
kprintf("\e[92minitialized %u type descriptors\n", ktid_last);
#endif
}
void __kt_register(ktDescriptor* descriptor){
descriptor->id=++ktid_last;
Autoarr_add(__descriptorPointers, descriptor);
}
ktDescriptor* ktDescriptor_get(ktid id){
if(id>ktid_last || id==ktid_undefined) {
kprintf("\ntype id: %u\n",id);
throw("invalid type id");
}
return typeDescriptors[id];
}
void kt_free(){
free(typeDescriptors);
}

View File

@@ -0,0 +1,49 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
#include "../std.h"
#include "ktid.h"
#include "ktDescriptor.h"
extern ktid ktid_last;
void __kt_register(ktDescriptor* descriptor);
#define kt_register(TYPE) \
__kt_register(&ktDescriptor_##TYPE); \
ktid_##TYPE=ktid_last; \
__kt_register(&ktDescriptor_##TYPE##_Ptr); \
ktid_##TYPE##_Ptr=ktid_last;
void kt_beginInit();
void kt_endInit();
/// @param id id of registered type
ktDescriptor* ktDescriptor_get(ktid id);
char* ktDescriptor_toString(ktDescriptor* d);
// call it to free heap-allocated ktDescriptors array
void kt_free();
kt_declare(Pointer);
kt_declare(char);
kt_declare(bool);
kt_declare(f32);
kt_declare(f64);
kt_declare(i8);
kt_declare(u8);
kt_declare(i16);
kt_declare(u16);
kt_declare(i32);
kt_declare(u32);
kt_declare(i64);
kt_declare(u64);
kt_declare(ktDescriptor);
#if __cplusplus
}
#endif

View File

@@ -0,0 +1,28 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
#include "../std.h"
#include "typedef_macros.h"
typedef u16 ktid;
static const ktid ktid_undefined=-1;
/// gets descriptor id for TYPE
#define ktid_name(TYPE) ktid_##TYPE
/// gets descriptor id for pointer to TYPE
#define ktid_ptrName(TYPE) ktid_##TYPE##_Ptr
#define ktid_declare(TYPE) \
extern ktid ktid_##TYPE; \
extern ktid ktid_##TYPE##_Ptr;
#define ktid_define(TYPE) \
ktid ktid_##TYPE=-1; \
ktid ktid_##TYPE##_Ptr=-1;
#if __cplusplus
}
#endif

View File

@@ -0,0 +1,8 @@
#pragma once
#include "init.h"
#include "ktid.h"
#include "ktDescriptor.h"
#include "kt_functions.h"
#include "unitype.h"
#include "typedef_macros.h"

View File

@@ -0,0 +1,15 @@
#pragma once
#define ENUM(ENUM_NAME, ENUM_MEMBERS...) typedef enum ENUM_NAME { \
ENUM_MEMBERS \
} ENUM_NAME;
#define PACKED_ENUM(ENUM_NAME, ENUM_MEMBERS...) typedef enum ENUM_NAME { \
ENUM_MEMBERS \
} __attribute__((__packed__)) ENUM_NAME;
#define STRUCT(STRUCT_NAME, STRUCT_MEMBERS...) typedef struct STRUCT_NAME STRUCT_NAME; \
typedef struct STRUCT_NAME { \
STRUCT_MEMBERS \
} STRUCT_NAME; \
kt_declare(STRUCT_NAME);

View File

@@ -0,0 +1,100 @@
#include "../../kprint/kprint_format.h"
#include "../base.h"
char *__Unitype_toString(void *_u, u32 fmt)
{
return Unitype_toString(*(Unitype *)_u, fmt);
}
kt_define(Unitype, __UnitypePtr_free, __Unitype_toString);
void Unitype_free(Unitype u)
{
if (u.typeId == ktid_undefined)
{
if (u.VoidPtr != NULL)
throw("unitype with undefined typeId has value");
return;
}
ktDescriptor *type = ktDescriptor_get(u.typeId);
if (type->freeMembers)
type->freeMembers(u.VoidPtr);
if (u.allocatedInHeap)
free(u.VoidPtr);
}
void __UnitypePtr_free(void *u)
{
Unitype_free(*(Unitype *)u);
}
char *Unitype_toString(Unitype u, u32 fmt)
{
if (u.typeId == ktid_undefined)
{
if (u.VoidPtr != NULL)
throw("unitype with undefined typeId has value");
return cptr_copy("{ERROR_TYPE}");
}
if (fmt == 0)
{
if (u.typeId == ktid_name(bool) || u.typeId == ktid_name(i8) || u.typeId == ktid_name(i16) ||
u.typeId == ktid_name(i32) || u.typeId == ktid_name(i64))
{
// auto format set
fmt = kp_i;
// replaces value with pointer to value to pass into toString_i64(void*, u32)
i64 value = u.Int64;
u.VoidPtr = &value;
}
else if (u.typeId == ktid_name(u8) || u.typeId == ktid_name(u16) || u.typeId == ktid_name(u32) ||
u.typeId == ktid_name(u64))
{
fmt = kp_u;
u64 value = u.UInt64;
u.VoidPtr = &value;
}
else if (u.typeId == ktid_name(f32) || u.typeId == ktid_name(f64))
{
fmt = kp_f;
f64 value = u.Float64;
u.VoidPtr = &value;
}
else if (u.typeId == ktid_name(char))
{
fmt = kp_c;
i64 value = u.Int64;
u.VoidPtr = &value;
}
else if (u.typeId == ktid_ptrName(char))
{
fmt = kp_s;
}
else if (u.typeId == ktid_name(Pointer))
{
if (u.VoidPtr == NULL)
return cptr_copy("{ UniNull }");
fmt = kp_h;
}
}
ktDescriptor *type = ktDescriptor_get(u.typeId);
char *valuestr;
if (type->toString)
valuestr = type->toString(u.VoidPtr, fmt);
else
valuestr = "ERR_NO_TOSTRING_FUNC";
char *rezult = cptr_concat("{ type: ", type->name, ", allocated on heap: ", (u.allocatedInHeap ? "true" : "false"),
", value:", valuestr, " }");
if (type->toString)
free(valuestr);
return rezult;
}
void printuni(Unitype v)
{
char *s = Unitype_toString(v, 0);
fputs(s, stdout);
free(s);
}

View File

@@ -0,0 +1,55 @@
#pragma once
#if __cplusplus
extern "C" {
#endif
#include "ktid.h"
#include "typedef_macros.h"
STRUCT(Unitype,
union {
i64 Int64;
u64 UInt64;
f64 Float64;
bool Bool;
void* VoidPtr;
char Bytes[8];
};
ktid typeId;
bool allocatedInHeap; // should Unitype_free call free() to VoidPtr*
)
#define __UniDef(FIELD, TYPE, VAL) ((Unitype){ \
.FIELD=VAL, .typeId=ktid_name(TYPE), .allocatedInHeap=false})
#define UniInt64(VAL) __UniDef(Int64, i64, VAL)
#define UniUInt64(VAL) __UniDef(UInt64, u64, VAL)
#define UniFloat64(VAL) __UniDef(Float64, f64, VAL)
#define UniBool(VAL) __UniDef(Bool, bool, VAL)
#define UniPtr(TYPE_ID, VAL, ALLOCATED_ON_HEAP)((Unitype){ \
.VoidPtr=VAL, .typeId=TYPE_ID, .allocatedInHeap=ALLOCATED_ON_HEAP })
#define UniStackPtr(TYPE, VAL) UniPtr(ktid_ptrName(TYPE), VAL, false)
#define UniHeapPtr(TYPE, VAL) UniPtr(ktid_ptrName(TYPE), VAL, true)
// 0==ktid_Pointer
#define UniNull ((Unitype){.Int64=0, .typeId=0, .allocatedInHeap=false})
#define UniTrue UniBool(true)
#define UniFalse UniBool(false)
#define Unitype_isUniNull(UNI) (UNI.typeId==0 && UNI.Int64==0)
#define UniCheckTypeId(UNI, TYPE_ID) (UNI.typeId==TYPE_ID)
#define UniCheckType(UNI, TYPE) UniCheckTypeId(UNI, ktid_name(TYPE))
#define UniCheckTypePtr(UNI, TYPE) UniCheckTypeId(UNI, ktid_ptrName(TYPE))
// frees VoidPtr value or does nothing if type isn't pointer
void Unitype_free(Unitype u);
void __UnitypePtr_free(void* u);
char* Unitype_toString(Unitype v, u32 fmt);
void printuni(Unitype v);
#if __cplusplus
}
#endif