diff --git a/build_linux64/libamsstring4.linux64.a b/build_linux64/libamsstring4.linux64.a index 19c13f7..f187fb1 100644 Binary files a/build_linux64/libamsstring4.linux64.a and b/build_linux64/libamsstring4.linux64.a differ diff --git a/build_linux64/objstore/amsstring4_class.o b/build_linux64/objstore/amsstring4_class.o new file mode 100644 index 0000000..cf6642c Binary files /dev/null and b/build_linux64/objstore/amsstring4_class.o differ diff --git a/build_linux64/objstore/amsstring4_portability.o b/build_linux64/objstore/amsstring4_portability.o new file mode 100644 index 0000000..3a5600b Binary files /dev/null and b/build_linux64/objstore/amsstring4_portability.o differ diff --git a/build_linux64/objstore/amsstring4_tests1.o b/build_linux64/objstore/amsstring4_tests1.o new file mode 100644 index 0000000..c3a559c Binary files /dev/null and b/build_linux64/objstore/amsstring4_tests1.o differ diff --git a/build_linux64/tests b/build_linux64/tests index 64aebfb..d6fec7b 100644 Binary files a/build_linux64/tests and b/build_linux64/tests differ diff --git a/include/amsstring4/amsstring4.hpp b/include/amsstring4/amsstring4.hpp index dfddd1a..d695a60 100644 --- a/include/amsstring4/amsstring4.hpp +++ b/include/amsstring4/amsstring4.hpp @@ -4,11 +4,163 @@ #include #include #include +#include +#include +#include +#include +#include +#include + +#include namespace ams { +//wraps the functions strcpy_s and strncpy in a portable manner +//between linux and microsoft standard C libraries. +int amsstrcpy_s(char *dest, int size, const char *src); + +//wraps the functions sprintf_s and snprintf in a portable manner +//between linux and microsoft standard C libraries. +int amssprintf_s(char *s, int n, const char *format, ...); + +//Using the C library's sscanf function is more robust +//than atod or atof. It returns valid numbers for infs and nans +//Returns nan for any uninterpretable string +double amsstrtonum(const char *str); + +typedef char ams_chartype; +static const ams_chartype ams_char_cr = (ams_chartype) '\r'; //carriage return +static const ams_chartype ams_char_lf = (ams_chartype) '\n'; //newline +static const ams_chartype ams_char_tb = (ams_chartype) '\t'; //tab +static const ams_chartype ams_char_nt = (ams_chartype) '\0'; //null terminator + +class amsstring +{ +public: + + ams_chartype blank; // null terminator returned for accessing index out of bounds + ams_chartype *cstring; + int length; + //length will be set to the length of the cstring not including the null terminating char + + //Basic functions + amsstring(); + ~amsstring(); + + amsstring(amsstring &other); + amsstring& operator=(amsstring &other); + amsstring(const amsstring &other); + const amsstring& operator=(const amsstring &other); + + amsstring(ams_chartype *other); + amsstring(const ams_chartype *other); + + amsstring& operator=(ams_chartype *other); + const amsstring& operator=(const ams_chartype *other); //assign string constant to amsstring + //const amsstring& operator=(const ams_chartype *other) const; //assign string constant to amsstring + //const is a disease! + + // + //amsstring(int length); + //amsstring(int length, const ams_chartype initchar); + + int resize(const int newlen); + int size() const; + + ams_chartype& operator[](const int ind); + const ams_chartype& operator[](const int ind) const; + ams_chartype& at(const int ind); + const ams_chartype& at(const int ind) const; + + void clear(); + void setall(const ams_chartype val, const int newlen); + void shrinktofit(); + + //string comparisons + bool operator==(const amsstring &other) const; + bool operator==(const char *other) const; + bool operator!=(const amsstring &other) const; + bool operator!=(const char *other) const; + + //string ordering comparison + //alphebetizes strings by ASCII character + //longer strings compare larger than shorter ones + bool operator<(const amsstring &other) const; + bool operator>(const amsstring &other) const; + bool operator<(const ams_chartype *other) const; + bool operator>(const ams_chartype *other) const; + + //Insert, Remove, and Substring + void insert(const int ind, const amsstring other); + void insert(const int ind, const ams_chartype *other); + void remove(const int ind); + void remove(const int ind1, const int ind2); + void substring(const int ind1, const int ind2, amsstring *sout) const; + + //Append + void append(const amsstring &other); + void append(const ams_chartype *other); + void append(const ams_chartype other); + + amsstring operator+(const amsstring &other); + const amsstring operator+(const amsstring &other) const; + amsstring operator+(const ams_chartype *other); + const amsstring operator+(const ams_chartype *other) const; + amsstring operator+(const ams_chartype other); + const amsstring operator+(const ams_chartype other) const; + + //Find + int find(const amsstring findstr, const int indstart=0, const bool casesens=1) const; + int find(const ams_chartype *findstr, const int indstart=0, const bool casesens=1) const; + int find(const ams_chartype c, const int indstart=0, const bool casesens=1) const; + + //formatted input + int sprintf(int bufflen, const ams_chartype *formatstring, ...); + + void tolower(); + void toupper(); + + bool isvalidnumber(); + double strtonum(); }; +//needs work +void splitlines(amsstring *s, std::vector *lns); +void splitlines(amsstring *s, ams::amsarray *lns); + +void split(amsstring *s, const ams_chartype delimitchar, std::vector *lns); +void split(amsstring *s, const ams_chartype delimitchar, ams::amsarray *lns); + +void split(amsstring *s, const ams_chartype *delimitstr, std::vector *lns); +void split(amsstring *s, const ams_chartype *delimitstr, ams::amsarray *lns); + +void split(amsstring *s, amsstring *delimitstr, std::vector *lns); +void split(amsstring *s, amsstring *delimitstr, ams::amsarray *lns); + +//splits a string, not counting whitespaces between non-whitespace characters +void splitwhitespace(amsstring *s, std::vector *lns); +void splitwhitespace(amsstring *s, ams::amsarray *lns); + +//removes all whitespace characters '\t','\r','\n' included +//to the left and right of the string (but not in the middle) +void stripwhitespace(amsstring *s); + +//completely removes all whitespace entirely +void stripallwhitespace(amsstring *s); + +void freadline(FILE *fp, amsstring *s); +void freadlines(FILE *fp, std::vector *lines); +void fwritelines(FILE *fp, amsstring *s); +void fwritelines(FILE *fp, std::vector *lines); +void freadtxtfile(FILE *fp, amsstring *s); + +}; //end namespace ams + +#include +#include +#include + + #endif \ No newline at end of file diff --git a/include/amsstring4/amsstring4_bintextencoding.hpp b/include/amsstring4/amsstring4_bintextencoding.hpp new file mode 100644 index 0000000..4d748df --- /dev/null +++ b/include/amsstring4/amsstring4_bintextencoding.hpp @@ -0,0 +1,28 @@ +#ifndef __AMSSTRING4_BINTEXTENCODING_HPP__ +#define __AMSSTRING4_BINTEXTENCODING_HPP__ + +namespace ams +{ + +//Only processes strings of length divisible by 4, with +//expected 0,1,2 padding chars at the end of the string, +//and no non-coding characters. +void base64encode(ams::amsarray *bytes, amsstring *str); + + + +int base64decode(amsstring *str, ams::amsarray *bytes, bool bstrict); + +//decodes, ignoring (as in MIME spec) all characters that are not +//valid b64 alphabet chars, and all padding until the end of the string +int base64decode_liberal(amsstring *str, ams::amsarray *bytes); + +int base64decode_strict(amsstring *str, ams::amsarray *bytes); + +void test_base64encode(); +void test_base64encode_fuzztest(); + +}; + +#endif + diff --git a/include/amsstring4/amsstring4_tests.hpp b/include/amsstring4/amsstring4_tests.hpp new file mode 100644 index 0000000..f064da7 --- /dev/null +++ b/include/amsstring4/amsstring4_tests.hpp @@ -0,0 +1,27 @@ +#ifndef __AMSSTRING4_TESTS_HPP__ +#define __AMSSTRING4_TESTS_HPP__ + +namespace ams +{ + + void amsstring3_basic_string_test1(); + void amsstring3_sscanf_test1(); + void amsstring3_basic_string_test2(); + void amsstring3_memoryleakcheck1(); + void amsstring3_memoryleakcheck2(); + void amsstring3_stringtests2(); + void amsstring3_test_find(); + + void amsstring3_test_splitlines(); + void amsstring3_test_split(); + void amsstring3_test_strip(); + void amsstring3_test_freadwrite(); + + void amsstring3_test_concatenation_operators(); + + + +}; + +#endif + diff --git a/include/amsstring4/amsstring4_unicode.hpp b/include/amsstring4/amsstring4_unicode.hpp new file mode 100644 index 0000000..d446359 --- /dev/null +++ b/include/amsstring4/amsstring4_unicode.hpp @@ -0,0 +1,22 @@ +#ifndef __AMSSTRING4_UNICODE_HPP__ +#define __AMSSTRING4_UNICODE_HPP__ + +namespace ams +{ + +int string_to_uccodepoints(const amsstring &str, amsarray &codepoints); +int string_to_uccodepoints(const amsstring *str, amsarray *codepoints); + +void uccodepoints_to_string(const amsarray &codepoints, amsstring &str); +void uccodepoints_to_string(const amsarray *codepoints, amsstring *str); + +void test_unicode_ascii_int_conv(); +void test_unicode_conv1(); + +void test_unicode_conv2(); + + +}; + +#endif + diff --git a/src/amsstring4/amsstring4_bintextencoding.cpp b/src/amsstring4/amsstring4_bintextencoding.cpp new file mode 100644 index 0000000..2340d5b --- /dev/null +++ b/src/amsstring4/amsstring4_bintextencoding.cpp @@ -0,0 +1,565 @@ +#include + +namespace ams +{ + +//PGP / GPG text armor, binary encoding scheme: +// + +//HTML embedded image file binary encoding scheme: +//example: +// Embedded Image +// ref: https://stackoverflow.com/questions/11474346/how-to-encode-images-within-html +// Embed other stuff! +// Data URIs can potentially store any type of data, not just images! Try these examples on for size: (X)HTML CSS Embedding Example +// +// (X)HTML Javascript Embedding Example +// + +//I think these are both base-64 encodings of a binary blob +//ref: https://security.stackexchange.com/questions/142043/how-are-pgp-messages-constructed + +//also widely used for email attachments + +//Base64: 3 bytes (24-bits) 11111111 22222222 33333333 +// converted to 4 base-64 digits +// 111111 112222 222233 333333 +// subtleties on termination of a string of bytes that doesn't divide by 3 + +//real reference: +// https://datatracker.ietf.org/doc/html/rfc4648#section-4 + + + // Table 1: The Base 64 Alphabet + + // Value Encoding Value Encoding Value Encoding Value Encoding + // 0 A 17 R 34 i 51 z + // 1 B 18 S 35 j 52 0 + // 2 C 19 T 36 k 53 1 + // 3 D 20 U 37 l 54 2 + // 4 E 21 V 38 m 55 3 + // 5 F 22 W 39 n 56 4 + // 6 G 23 X 40 o 57 5 + // 7 H 24 Y 41 p 58 6 + // 8 I 25 Z 42 q 59 7 + // 9 J 26 a 43 r 60 8 + // 10 K 27 b 44 s 61 9 + // 11 L 28 c 45 t 62 + + // 12 M 29 d 46 u 63 / + // 13 N 30 e 47 v + // 14 O 31 f 48 w (pad) = + // 15 P 32 g 49 x + // 16 Q 33 h 50 y + + +//ord is 0-63, 64 for '=' +ams_chartype base64_char(int8_t ord) +{ + ams_chartype ret; + if(ord==64) + { + ret = '='; + } + else if(ord>=0&&ord<26) + { + ret = (ams_chartype)(ord+65); + } + else if(ord>=26&&ord<52) + { + ret = (ams_chartype)((ord-26)+97); + } + else if(ord>=52&&ord<62) + { + ret = (ams_chartype)((ord-52)+48); + } + else if(ord==62) + ret = '+'; + else if(ord==63) + ret = '/'; + else + ret = '\0'; //NULL character is for an out of range ord + return ret; +} + +int8_t base64_ord(ams_chartype ch) +{ + uint8_t ret; + uint8_t ucval = (unsigned char) ch; + if(ucval>=65 && ucval<91) + { + ret = ucval-65; + } + else if(ucval>=97 && ucval<123) + { + ret = ucval-97+26; + } + else if(ucval>=48 && ucval<58) + { + ret = ucval-48+52; + } + else if(ch=='+') + { + ret = 62; + } + else if(ch=='/') + { + ret = 63; + } + else if(ch=='=') + { + ret = 64; + } + else + { + ret = -1; + } + + return ret; +} + +static void b64_encode_3byteblock(uint8_t *bytes, ams_chartype *chars) +{ + uint8_t o1,o2,o3,o4; + + o1 = ((bytes[0] & 0b11111100)>>2); + o2 = (((bytes[0] & 0b00000011)<<4) + ((bytes[1] & 0b11110000)>>4)); + o3 = (((bytes[1] & 0b00001111)<<2) + ((bytes[2] & 0b11000000)>>6)); + o4 = ((bytes[2] & 0b00111111)); + + chars[0] = base64_char(o1); + chars[1] = base64_char(o2); + chars[2] = base64_char(o3); + chars[3] = base64_char(o4); + + return; +} + +static void b64_encode_2byteblock(uint8_t *bytes, ams_chartype *chars) +{ + uint8_t o1,o2,o3; + + o1 = ((bytes[0] & 0b11111100)>>2); + o2 = (((bytes[0] & 0b00000011)<<4) + ((bytes[1] & 0b11110000)>>4)); + o3 = (((bytes[1] & 0b00001111)<<2)); + + chars[0] = base64_char(o1); + chars[1] = base64_char(o2); + chars[2] = base64_char(o3); + chars[3] = '='; + + return; +} + +static void b64_encode_1byteblock(uint8_t *bytes, ams_chartype *chars) +{ + uint8_t o1,o2; + + o1 = ((bytes[0] & 0b11111100)>>2); + o2 = (((bytes[0] & 0b00000011)<<4)); + + chars[0] = base64_char(o1); + chars[1] = base64_char(o2); + chars[2] = '='; + chars[3] = '='; + + return; +} + +static int decode_4charblock(ams_chartype *chars, uint8_t *bytes) +{ + int ret = 1; + + uint8_t o1,o2,o3,o4,b1,b2,b3; + int len; + + o1 = base64_ord(chars[0]); + o2 = base64_ord(chars[1]); + o3 = base64_ord(chars[2]); + o4 = base64_ord(chars[3]); + + len = 3; + if(chars[3]=='=') {len--; o4 = 0;} + if(chars[2]=='=') {len--; o3 = 0;} + if(chars[1]=='=') {len--; o2 = 0;} + + if(len>0) + { + //111111 222222 333333 444444 + //11111122 22223333 33444444 + + b1 = ((o1<<2)+((o2 & 0b00110000)>>4)); + b2 = (((o2 & 0b00001111)<<4) + ((o3 & 0b00111100)>>2)); + b3 = (((o3 & 0b00000011)<<6) + (o4 & 0b00111111)); + if(len>=1) bytes[0] = b1; + if(len>=2) bytes[1] = b2; + if(len>=3) bytes[2] = b3; + } + + return ret; +} + +void base64encode(ams::amsarray *bytes, amsstring *str) +{ + long I,J; + + //str->resize((bytes->length*4)/3+2); + if( bytes->length % 3 == 0) + { + str->resize((bytes->length/3)*4); + } + else + { + str->resize((bytes->length/3+1)*4); + } + + I = 0; J = 0; + while(Ilength) + { + if(bytes->length-I>=3) + { + //printf("debug3: %c%c%c\n",bytes->data[I],bytes->data[I+1],bytes->data[I+2]); + b64_encode_3byteblock(&(bytes->data[I]),&(str->cstring[J])); + I = I + 3; + J = J + 4; + } + else if(bytes->length-I==2) + { + //printf("debug2: %c%c\n",bytes->data[I],bytes->data[I+1]); + b64_encode_2byteblock(&(bytes->data[I]),&(str->cstring[J])); + I = I + 2; + J = J + 4; + } + else if(bytes->length-I==1) + { + //printf("debug1: %c\n",bytes->data[I]); + b64_encode_1byteblock(&(bytes->data[I]),&(str->cstring[J])); + I = I + 1; + J = J + 4; + } + else + { + break; + } + } + + //str->shrinktofit(); + str->cstring[str->length] = '\0'; + + return; +} + +static long __intl_localstrlen(amsstring *str) +{ + long ret = 0; + long I; + for(I=0;Ilength;I++) + { + if(str->cstring[I]=='\0') + { + ret = I; + break; + } + } + return ret; +} + + +//Only processes strings of length divisible by 4, with +//expected 0,1,2 padding chars at the end of the string, +//and no non-coding characters. +int base64decode_strict(amsstring *str, ams::amsarray *bytes) +{ + int ret = 1; + int fail = 0; + long len; + long I,J; + uint8_t v; + int bp; + + if(str->length == 0) + { + bytes->resize(0); + ret = 1; + return ret; + } + + if(str->length % 4 !=0) + { + ret = -1; //invalid length + bytes->resize(0); + return ret; + } + + //resize bytes to indicated size + + + len = (str->length/4)*3; + + if(str->cstring[str->length-1]=='=') len--; + if(str->cstring[str->length-2]=='=') len--; + if(str->cstring[str->length-3]=='=') + { + ret = -2; //unexpected number of padding chars + bytes->resize(0); + return ret; + } + + bytes->resize(len); + + fail = 0; + I = 0; J = 0; + //for(I=0;Ilength && fail==0;I++) + while(Ilength && fail==0) + { + for(bp=0;bp<4;bp++) + { + v = base64_ord(str->cstring[I+bp]); + //printf("debug: %d %d %c %d\n",I,I+bp,str->cstring[I+bp],v); + if(v<0 || (v>=64 && (I+bp)length-2) || v>=65) + { + //printf("dbg: fail!\n"); + fail = 1; + break; + } + } + decode_4charblock(&(str->cstring[I]),&(bytes->data[J])); + + // for(bp=0;bp<3;bp++) + // { + // printf("debug: %d %d %c\n",J+bp,bytes->data[J+bp],bytes->data[J+bp]); + // } + + I = I + 4; + J = J + 3; + } + if(fail==1) + { + ret = -3; //invalid char encountered + bytes->resize(0); + } + + //fail on any non-coding chars while parsing + + return ret; +} + +//decodes, ignoring (as in MIME spec) all characters that are not +//valid b64 alphabet chars, and all padding until the end of the string +int base64decode_liberal(amsstring *str, ams::amsarray *bytes) +{ + int ret = 1; + long I,J; + amsstring str2; + ams_chartype ch; + + int64_t v; + + str2.resize(str->length+4); + + //filter chars in str to remove all non-coding chars + J = 0; + for(I=0;Ilength;I++) + { + ch = str->cstring[I]; + if(ch=='\0') + break; + + v = base64_ord(ch); + if(v>=0 && v<64) + { + str2.cstring[J] = ch; + J = J + 1; + } + } + + if(J%4==0) + { + str2.resize(J); + str2.cstring[str2.length] = '\0'; + } + if(J%4==1) + { + ret = 0; //this isn't really valid + str2.cstring[J] = '='; J++; + str2.cstring[J] = '='; J++; + str2.cstring[J] = '='; J++; + str2.resize(J); + str2.cstring[str2.length] = '\0'; + } + if(J%4==2) + { + str2.cstring[J] = '='; J++; + str2.cstring[J] = '='; J++; + str2.resize(J); + str2.cstring[str2.length] = '\0'; + } + if(J%4==3) + { + str2.cstring[J] = '='; J++; + str2.resize(J); + str2.cstring[str2.length] = '\0'; + } + + str2.shrinktofit(); + + ret = base64decode_strict(&str2,bytes); + + return ret; +} + +static void _intl_convsb(amsstring *s, amsarray *b) +{ + long I; + b->resize(s->length); + for(I=0;Ilength;I++) + { + b->at(I) = (unsigned char) s->cstring[I]; + } + return; +} + +static void _intl_convbs(amsarray *b,amsstring *s) +{ + long I; + s->resize(b->length); + for(I=0;Ilength;I++) + { + s->cstring[I] = b->data[I]; + } + s->cstring[s->length] = '\0'; + return; +} + +void test_base64encode1() +{ + int I; + int8_t o1,o2; + ams_chartype c1,c2; + + for(I=-5;I<70;I++) + { + o1 = I; + c1 = base64_char(o1); + o2 = base64_ord(c1); + c2 = base64_char(o2); + + printf("%d %d %c %d %c\n",(int)I,(int)o1,c1,(int)o2,c2); + } + return; +} + +//segmentation faults in decode_liberal + +void test_base64encode() +{ + printf("Tests of base64 encoding/deconding.\n"); + + amsstring s1,s1e,s2; + amsarray b1,b2; + int ret = 0; + bool bstrict = 1; + + s1 = "light work."; + _intl_convsb(&s1,&b1); + base64encode(&b1,&s1e); + ret = base64decode(&s1e,&b2,bstrict); + _intl_convbs(&b2,&s2); + + // for(I=0;I bytes,bytes2; + amsstring str,str2; + + int passed = 0; + int failed = 0; + + for(I=0;I<100;I++) + { + printf("Test %d\n",(int)I); + bytes.resize(I); + for(J=0;J *bytes, bool bstrict=0) +{ + int ret = 0; + if(bstrict==1) + { + ret = base64decode_strict(str,bytes); + } + else + { + ret = base64decode_liberal(str,bytes); + } + return ret; +} + + +}; \ No newline at end of file diff --git a/src/amsstring4/amsstring4_class.cpp b/src/amsstring4/amsstring4_class.cpp new file mode 100644 index 0000000..22b553e --- /dev/null +++ b/src/amsstring4/amsstring4_class.cpp @@ -0,0 +1,1692 @@ +#include + +namespace ams +{ + + ams_chartype tolower(ams_chartype c) + { + unsigned char c2 = (unsigned char) c; + ams_chartype ret; + if(c2>=65&&c2<=90) + { + ret = (ams_chartype) (c2+(97-65)); + } + else + { + ret = c; + } + return ret; + } + + ams_chartype toupper(ams_chartype c) + { + unsigned char c2 = (unsigned char) c; + ams_chartype ret; + if(c2>=97&&c2<=122) + { + ret = (ams_chartype) (c2-(97-65)); + } + else + { + ret = c; + } + return ret; + } + + static int localstrlen(const ams_chartype *c) + { + int I; + if(c!=NULL) + { + I = 0; + while(c[I]!='\0') + { + I++; + } + return I; + //return strlen((const char*) c); + } + + else + return 0; + } + + bool amsstrneq(const ams_chartype *s1, const ams_chartype *s2, const bool casesens=1) + { + bool ret = 0; + int N1 = localstrlen(s1); + int N2 = localstrlen(s2); + bool b1,b2; + + if(N1==N2) + { + int I; + bool q = 1; + for(I=0;Iblank = (ams_chartype) '\0'; + str->cstring = NULL; + str->cstring = new(std::nothrow) ams_chartype[1]; + str->cstring[0] = (ams_chartype) '\0'; + str->length = 0; + return; + } + + amsstring::amsstring() + { + //amsstring_init(this); + + blank = (ams_chartype) '\0'; + cstring = NULL; + cstring = new(std::nothrow) ams_chartype[1]; + cstring[0] = (ams_chartype) '\0'; + length = 0; + + return; + } + + amsstring::~amsstring() + { + if(cstring!=NULL) + { + delete[] cstring; + cstring = NULL; + } + length = 0; + blank = '\0'; + + return; + } + + int amsstring::size() const + { + return length; + } + + //Rewrite amsstring::resize() - valgrind is still complaining! + int amsstring::resize(const int newlen) + { + int ret = 0; + int I = 0; + ams_chartype *newbuff = NULL; + + if(newlen == length) + { + //sizes are the same, do nothing + ret = 1; + return ret; + } + + if(newlen<=0) //if new length <0 + { + newbuff = new(std::nothrow) ams_chartype[1]; + if(newbuff==NULL) + { + ret = -1; + return ret; + } + newbuff[0] = '\0'; + + if(cstring!=NULL) {delete[] cstring; cstring = NULL;} + cstring = newbuff; + length = 0; + ret = 1; + return ret; + } + + newbuff = new(std::nothrow) ams_chartype[newlen+1]; + if(newbuff==NULL) + { + ret = -1; + return ret; + } + + //Copy old data + for(I=0;I=0) + // { + // newbuf = new(std::nothrow) ams_chartype[newlen+1]; + + // if(newbuf==NULL) + // { + // ret = -1; //new buffer would not allocate + // } + // else + // { + // for(I=0;I=0) + // { + // ret = this->resize(0); + // } + + // //printf("DEBUG: ret = %d\n",ret); + // return ret; + // } + + amsstring::amsstring(const amsstring &other) + { + //amsstring_init(this); + blank = (ams_chartype) '\0'; + cstring = NULL; + cstring = new(std::nothrow) ams_chartype[1]; + cstring[0] = (ams_chartype) '\0'; + length = 0; + + + if(this!=&other) + { + *this=other; + } + return; + } + + amsstring::amsstring(amsstring &other) + { + //amsstring_init(this); + + blank = (ams_chartype) '\0'; + cstring = NULL; + cstring = new(std::nothrow) ams_chartype[1]; + cstring[0] = (ams_chartype) '\0'; + length = 0; + + if(this!=&other) + { + *this=other; + } + return; + } + + const amsstring& amsstring::operator=(const amsstring &other) + { + if(this!=&other) + { + this->resize(other.length); + amsstrcpy_s(cstring,length+1,other.cstring); + cstring[length] = (ams_chartype) '\0'; + shrinktofit(); //shrinks if the other cstring was corrupted with internal null terminators + + // N = strlen(cstring); //if string is actually shorter (contains null terminating character elsewhere than at the end) + // //then resize to string's size + // if(Nresize(N); + // } + } + return *this; + } + + amsstring& amsstring::operator=(amsstring &other) + { + if(this!=&other) + { + this->resize(other.length); + amsstrcpy_s(cstring,length+1,other.cstring); + cstring[length] = (ams_chartype) '\0'; + shrinktofit(); //shrinks if the other cstring was corrupted with internal null terminators + + // N = strlen(cstring); //if string is actually shorter (contains null terminating character elsewhere than at the end) + // //then resize to string's size + // if(Nresize(N); + // } + } + return *this; + } + + amsstring::amsstring(ams_chartype *other) + { + //amsstring_init(this); + blank = (ams_chartype) '\0'; + cstring = NULL; + cstring = new(std::nothrow) ams_chartype[1]; + cstring[0] = (ams_chartype) '\0'; + length = 0; + + *this = other; + + return; + } + + amsstring::amsstring(const ams_chartype *other) + { + //amsstring_init(this); + blank = (ams_chartype) '\0'; + cstring = NULL; + cstring = new(std::nothrow) ams_chartype[1]; + cstring[0] = (ams_chartype) '\0'; + length = 0; + + *this = other; + + return; + } + + const amsstring& amsstring::operator=(const ams_chartype *other) + { + //amsstring_init(this); <-- init only on a constructor, otherwise memory leak + + int nl,q; + nl = localstrlen(other); + if(nl>=0) + { + q = this->resize(nl); + if(q>=0) + { + //printf("DEBUG: q=%d, length=%d",q,length); + amsstrcpy_s(cstring,length+1,other); + cstring[length] = (ams_chartype) '\0'; + } + else + { + this->resize(0); + } + } + else + { + //already of zero size + } + + return *this; + } + + amsstring& amsstring::operator=(ams_chartype *other) + { + //amsstring_init(this); <-- init only on a constructor, otherwise memory leak + + int nl,q; + nl = localstrlen(other); + if(nl>=0) + { + q = this->resize(nl); + if(q>=0) + { + //printf("DEBUG: q=%d, length=%d",q,length); + amsstrcpy_s(cstring,length+1,other); + cstring[length] = (ams_chartype) '\0'; + } + else + { + this->resize(0); + } + } + else + { + //already of zero size + } + + return *this; + } + + // const amsstring& amsstring::operator=(const ams_chartype *other) const + // { + // amsstring_init(this); + + // int nl,q; + // nl = strlen(other); + // if(nl>=0) + // { + // q = this->resize(nl); + // if(q>=0) + // { + // printf("DEBUG: q=%d, length=%d",q,length); + // amsstrcpy_s(cstring,length+1,other); + // cstring[length] = (ams_chartype) '\0'; + // } + // else + // { + // this->resize(0); + // } + // } + // else + // { + // //already of zero size + // } + + // return *this; + // } + + ams_chartype& amsstring::operator[](const int ind) + { + // if(ind<0 || ind>length-1) + // { + // blank = (ams_chartype) '\0'; + // return blank; + // } + // else + // { + // return cstring[ind]; + // } + + if(ind<0 || ind>=(length+1)) + { + blank = (ams_chartype) '\0'; + return blank; + } + else + { + return cstring[ind]; //cstring buffer is of length length+1 + } + + } + + const ams_chartype& amsstring::operator[](const int ind) const + { + if(ind<0 || ind>=(length+1)) + { + //blank = (ams_chartype) '\0'; + return blank; + } + else + { + return cstring[ind]; //cstring buffer is of length length+1 + } + } + + ams_chartype& amsstring::at(const int ind) + { + if(ind<0 || ind>=length+1) + { + blank = (ams_chartype) '\0'; + return blank; + } + else + { + return cstring[ind]; + } + } + + const ams_chartype& amsstring::at(const int ind) const + { + if(ind<0 || ind>=length+1) + { + //blank = (ams_chartype) '\0'; + return blank; + } + else + { + return cstring[ind]; + } + } + + void amsstring::clear() + { + this->resize(0); + return; + } + + void amsstring::setall(const ams_chartype val, const int newlen) + { + int I; + this->resize(newlen); + for(I=0;Iresize(N); + return; + } + + bool amsstring::operator==(const amsstring &other) const + { + bool ret = 0; + if(this!=&other) + { + ret = amsstrneq(this->cstring,other.cstring); + } + else + { + ret = 1; + } + return ret; + } + + bool amsstring::operator!=(const amsstring &other) const + { + return !((*this)==other); + } + + bool amsstring::operator==(const char *other) const + { + bool ret = 0; + ret = amsstrneq(this->cstring,other); + return ret; + } + + bool amsstring::operator!=(const char *other) const + { + return !((*this)==other); + } + + static int _local_charcompare(const ams_chartype c1, const ams_chartype c2) + { + int ret = 0; + //-1 - c1 < c2 + //0 - c1 == c2 + //1 c1 > c2 + unsigned char cc1 = (unsigned char) c1; + unsigned char cc2 = (unsigned char) c2; + if(cc1cc2) ret = 1; + return ret; + } + + bool amsstring::operator<(const amsstring &other) const + { + bool ret = 0; + bool gt = 0; + int I; + for(I=0;Ilength&&Icstring[I],other.cstring[I]) == -1) + { + ret = 1; + break; + } + if(_local_charcompare(this->cstring[I],other.cstring[I]) == 1) + { + ret = 0; + gt = 1; + break; + } + } + if(ret==0&>==0) + { + if(this->length>other.length) + { + ret = 0; + } + else if(this->length(const amsstring &other) const + { + bool ret = 0; + bool lt = 0; + int I; + for(I=0;Ilength&&Icstring[I],other.cstring[I]) == -1) + { + ret = 0; + lt = 1; + break; + } + if(_local_charcompare(this->cstring[I],other.cstring[I]) == 1) + { + ret = 1; + break; + } + } + if(ret==0&<==0) + { + if(this->length>other.length) + { + ret = 1; + } + else if(this->length(const ams_chartype *other) const + { + amsstring q = other; + return (*this)>q; + } + + //inserts a string into the current string at a specified location index + //ind: [0,length] where + // 0 inserts the string before the first element + // length inserts the string after the last element [length-1] + // all other locations fail + void amsstring::insert(const int ind, const amsstring other) + { + int I,l1,l2; + l1 = this->length; + l2 = other.length; + + I = 0; + if(cstring==NULL) l1 = 0; + if(other.cstring==NULL) l2 = 0; + + ams_chartype *newbuf=NULL; + + if(ind>=0 && ind<=this->length) + { + newbuf = new(std::nothrow) ams_chartype[l1+l2+1]; + if(newbuf==NULL) + { + //something went wrong with the resize + return; + } + + for(I=0;Ishrinktofit(); + } + + return; + } + + void amsstring::insert(const int ind, const ams_chartype *other) + { + amsstring q = other; + insert(ind,q); + return; + } + + void amsstring::remove(const int ind) + { + if(ind>=0 && ind(0,ind1); + I2 = ams::min(length,ind2); + N = length-(I2-I1); + ams_chartype *newbuf = NULL; + if(N>0) + { + newbuf = new ams_chartype[N+1]; + for(I=0;I(0,ind1); + I2 = ams::min(ind2,length); + N = (I2-I1); + if(N>0) + { + q.resize(N); + for(I=I1;Iresize(0); + } + } + else + { + I1 = ams::max(0,ind1); + I2 = ams::min(ind2,length); + N = (I2-I1); + if(N>0) + { + sout->resize(N); + for(I=I1;Icstring[I-I1] = cstring[I]; + } + sout->cstring[N] = '\0'; + } + else + { + sout->resize(0); + } + } + sout->cstring[sout->length] = '\0'; + + return; + } + + void amsstring::append(const amsstring &other) + { + this->insert(this->length,other); + return; + } + + void amsstring::append(const ams_chartype *other) + { + this->insert(this->length,other); + return; + } + + void amsstring::append(const ams_chartype other) + { + amsstring q; + q.resize(1); + q.cstring[0] = other; + this->insert(this->length,q); + return; + } + + int amsstring::find(const ams_chartype c, const int indstart, const bool casesens) const + { + int I; + int ret = -1; + bool b1,b2; + for(I=indstart;I=N) + { + ret = I-N+1; + qf = 1; + break; + } + + if(cs) + { + I = I + 1; + } + } + + return ret; + } + + void amsstring::tolower() + { + std::locale loc; + + int I; + for(I=0;Icstring,"%lf",&d); + if(q==0) + { + ret = 0; + } + else + { + ret = 1; + } + } + catch(int e) + { + ret = 0; + } + return ret; + } + + double amsstring::strtonum() + { + return amsstrtonum(this->cstring); + } + + + void splitlines(amsstring *s, std::vector *lns) + { + int I,I1,I2; + amsstring q; + int mode; + ams_chartype c; + + lns->resize(0); + + I1 = 0; + I = 0; + mode = 0; + while(Isize()) + { + c = s->at(I); + if(mode==0 && c==ams_char_cr) + { + mode=1; + I2 = I; + } + else if(mode==0 && c==ams_char_lf) + { + mode = 0; + I2 = I; + s->substring(I1,I2,&q); + lns->push_back(q); + I1 = I+1; + } + else if(mode==1 && c==ams_char_lf) + { + mode = 0; + s->substring(I1,I2,&q); + lns->push_back(q); + I1 = I+1; + } + else if(mode==1) + { + mode = 0; + } + + I = I + 1; + } + s->substring(I1,s->length,&q); + lns->push_back(q); + + return; + } + + void splitlines(amsstring *s, ams::amsarray *lns) + { + int I,I1,I2; + amsstring q; + int mode; + ams_chartype c; + + lns->resize(0); + + I1 = 0; + I = 0; + mode = 0; + while(Isize()) + { + c = s->at(I); + if(mode==0 && c==ams_char_cr) + { + mode=1; + I2 = I; + } + else if(mode==0 && c==ams_char_lf) + { + mode = 0; + I2 = I; + s->substring(I1,I2,&q); + lns->push_back(q); + I1 = I+1; + } + else if(mode==1 && c==ams_char_lf) + { + mode = 0; + s->substring(I1,I2,&q); + lns->push_back(q); + I1 = I+1; + } + else if(mode==1) + { + mode = 0; + } + + I = I + 1; + } + s->substring(I1,s->length,&q); + lns->push_back(q); + + return; + } + + /* + void splitlines(amsstring *s, std::vector *lns) + { + //windows uses \r\n, linux uses \n as a line delimiter + int I; + amsstring q; + lns->resize(0); + int ind1 = 0; + int ind2 = 0; + int mode = 0; + int cs; + I = 0; + ams_chartype c; + + + while(Ilength) + { + c = s->cstring[I]; + cs = 0; //has the character been 'consumed' by the state machine? + if(c=='\n'&&mode==0&&cs==0) + { + + cs = 1; + ind2 = I-1; + ind2 = ams::max(0,ind2); + s->substring(ind1,ind2+1,&q); + lns->push_back(q); + ind1 = I+1; + ind2 = I+1; + ind1 = ams::min(s->length,ind1); + ind2 = ams::min(s->length,ind2); + } + if(c=='\r'&&mode==0&&cs==0) + { + cs = 1; + mode = 1; + } + if(c=='\n'&&mode==1&&cs==0) + { + cs = 1; + ind2 = I-2; + ind2 = ams::max(0,ind2); + s->substring(ind1,ind2+1,&q); + lns->push_back(q); + ind1 = I+1; + ind2 = I+1; + ind1 = ams::min(s->length,ind1); + ind2 = ams::min(s->length,ind2); + mode = 0; + } + I = I + 1; + } + if(ind1length) + { + s->substring(ind1,s->length+1,&q); + lns->push_back(q); + } + + return; + } + */ + + void split(amsstring *s, const ams_chartype delimitchar, std::vector *lns) + { + amsstring q; + lns->resize(0); + + + int I = 0; + int ind1; + int ind2; + //int cs; + ind1 = 0; + ams_chartype c; + while(Ilength) + { + c = s->cstring[I]; + //cs = 0; + if(c==delimitchar) + { + ind2 = I; + ind2 = max(0,ind2); + s->substring(ind1,ind2,&q); + lns->push_back(q); + ind1 = I+1; + ind2 = I+1; + ind1 = min(s->length,ind1); + ind2 = min(s->length,ind2); + } + + I = I + 1; + } + if(ind1length&&s->cstring[ind1]!=delimitchar) + { + s->substring(ind1,s->length,&q); + lns->push_back(q); + } + + return; + } + + void split(amsstring *s, const ams_chartype delimitchar, ams::amsarray *lns) + { + amsstring q; + lns->resize(0); + + + int I = 0; + int ind1; + int ind2; + //int cs; + ind1 = 0; + ams_chartype c; + while(Ilength) + { + c = s->cstring[I]; + //cs = 0; + if(c==delimitchar) + { + ind2 = I; + ind2 = max(0,ind2); + s->substring(ind1,ind2,&q); + lns->push_back(q); + ind1 = I+1; + ind2 = I+1; + ind1 = min(s->length,ind1); + ind2 = min(s->length,ind2); + } + + I = I + 1; + } + if(ind1length&&s->cstring[ind1]!=delimitchar) + { + s->substring(ind1,s->length,&q); + lns->push_back(q); + } + + return; + } + + void split(amsstring *s, const ams_chartype *delimitstr, std::vector *lns) + { + amsstring q; + int I; + int ind1,ind2; + int cs; + int cnt; + ams_chartype c; + int N = localstrlen(delimitstr); + + if(lns!=NULL && delimitstr!=NULL) + { + if(N>0) + { + lns->resize(0); + + ind1 = 0; + I = 0; + cs = 0; + cnt = 0; + while(Isize()) + { + c = s->at(I); + if(cs==0 && c==delimitstr[cnt]) + { + cs=1; + ind2 = I; + cnt = cnt+1; + } + else if(cs==1 && c==delimitstr[cnt]) + { + cs=1; + cnt = cnt+1; + } + else if(cs==1 && c!=delimitstr[cnt]) + { + cs = 0; + ind2 = ind1; + cnt = 0; + I = I - 1; //step back so you can evaluate this char again + } + + if(cnt>=N) + { + //cut string from (ind1,ind2) + cs = 0; + + ind1 = min(ind1,s->size()); + ind1 = max(ind1,0); + ind2 = min(ind2,s->size()); + ind2 = max(ind2,0); + ind2 = max(ind2,ind1); + + s->substring(ind1,ind2,&q); + lns->push_back(q); + + ind1 = I + 1; + ind2 = I + 1; + cnt = 0; + } + I = I + 1; + } + + if(ind1size()) + { + s->substring(ind1,s->size(),&q); + lns->push_back(q); + } + } //N>0 + else + { + lns->push_back(*s); + } + } //lns!=NULL + + return; + } + + void split(amsstring *s, const ams_chartype *delimitstr, ams::amsarray *lns) + { + amsstring q; + int I; + int ind1,ind2; + int cs; + int cnt; + ams_chartype c; + int N = localstrlen(delimitstr); + + if(lns!=NULL && delimitstr!=NULL) + { + if(N>0) + { + lns->resize(0); + + ind1 = 0; + I = 0; + cs = 0; + cnt = 0; + while(Isize()) + { + c = s->at(I); + if(cs==0 && c==delimitstr[cnt]) + { + cs=1; + ind2 = I; + cnt = cnt+1; + } + else if(cs==1 && c==delimitstr[cnt]) + { + cs=1; + cnt = cnt+1; + } + else if(cs==1 && c!=delimitstr[cnt]) + { + cs = 0; + ind2 = ind1; + cnt = 0; + I = I - 1; //step back so you can evaluate this char again + } + + if(cnt>=N) + { + //cut string from (ind1,ind2) + cs = 0; + + ind1 = min(ind1,s->size()); + ind1 = max(ind1,0); + ind2 = min(ind2,s->size()); + ind2 = max(ind2,0); + ind2 = max(ind2,ind1); + + s->substring(ind1,ind2,&q); + lns->push_back(q); + + ind1 = I + 1; + ind2 = I + 1; + cnt = 0; + } + I = I + 1; + } + + if(ind1size()) + { + s->substring(ind1,s->size(),&q); + lns->push_back(q); + } + } //N>0 + else + { + lns->push_back(*s); + } + } //lns!=NULL + + return; + } + + void split(amsstring *s, amsstring *delimitstr, std::vector *lns) + { + split(s,delimitstr->cstring,lns); + return; + } + + void split(amsstring *s, amsstring *delimitstr, ams::amsarray *lns) + { + split(s,delimitstr->cstring,lns); + return; + } + + + void splitwhitespace(amsstring *s, std::vector *lns) + { + int I; + int mode; + ams_chartype c; + int ind1 = 0; + int ind2 = 0; + I = 0; + mode = 0; + amsstring q; + bool qf = 0; + + if(s!=NULL && lns!=NULL) + { + lns->resize(0); + while(qf==0) + { + c = s->cstring[I]; + if(!(isspace(c)||c==' '||c=='\t')&&mode==0) + { + mode = 1; + ind1 = I; + ind1 = ams::max(0,ind1); + } + if((isspace(c)||c==' '||c=='\t'||c=='\0')&&mode==1) + { + mode = 0; + ind2 = I; + ind2 = ams::min(ind2,s->length); + s->substring(ind1,ind2,&q); + //printf("debug: %d, %s\n",I,q.cstring); + lns->push_back(q); + } + if(I>=s->length) + { + qf = 1; + } + + I = I + 1; + } + } //test null pointers + return; + } + + void splitwhitespace(amsstring *s, ams::amsarray *lns) + { + int I; + int mode; + ams_chartype c; + int ind1 = 0; + int ind2 = 0; + I = 0; + mode = 0; + amsstring q; + bool qf = 0; + + if(s!=NULL && lns!=NULL) + { + lns->resize(0); + while(qf==0) + { + c = s->cstring[I]; + if(!(isspace(c)||c==' '||c=='\t')&&mode==0) + { + mode = 1; + ind1 = I; + ind1 = ams::max(0,ind1); + } + if((isspace(c)||c==' '||c=='\t'||c=='\0')&&mode==1) + { + mode = 0; + ind2 = I; + ind2 = ams::min(ind2,s->length); + s->substring(ind1,ind2,&q); + //printf("debug: %d, %s\n",I,q.cstring); + lns->push_back(q); + } + if(I>=s->length) + { + qf = 1; + } + + I = I + 1; + } + } //test null pointers + return; + } + + void stripwhitespace(amsstring *s) + { + int I; + amsstring q; + int ind1; + int ind2; + ams_chartype c; + + if(s!=NULL) + { + ind1 = 0; + ind2 = 0; + for(I=0;Ilength;I++) + { + c = s->cstring[I]; + if(!(isspace(c)||c==' '||c=='\t')) + { + ind1 = I; + break; + } + } + for(I=s->length-1;I>=0;I--) + { + c = s->cstring[I]; + if(!(isspace(c)||c==' '||c=='\t')) + { + ind2 = I+1; + break; + } + } + + ind1 = min(ind1,s->size()); + ind1 = max(ind1,0); + ind2 = min(ind2,s->size()); + ind2 = max(ind2,0); + ind2 = max(ind1,ind2); + + q = *s; //q.copy(s); + q.substring(ind1,ind2,s); + } //null guard + return; + } + + void stripallwhitespace(amsstring *s) + { + amsstring q; + q.resize(s->length); + //int count; + int I; + int J; + J = 0; + ams_chartype c; + for(I=0;Ilength;I++) + { + c = s->cstring[I]; + if(!(isspace(c)||c==' '||c=='\t'||c=='\n'||c=='\r'||c=='\f'||c=='\v'||(int)c==9||(int)c==10||(int)c==11||(int)c==12)) + { + q.cstring[J] = s->cstring[I]; + J = J + 1; + } + } + q.resize(J); + q.cstring[q.length]='\0'; + //s->copy(&q); + *s = q; + return; + } + + ///File IO + + void freadline(FILE *fp,amsstring *s) + { + const int BUFFSZ = 8000; + char buff[BUFFSZ]; + int N; + if(fp!=NULL && s!=NULL) + { + if(!feof(fp)) + { + fgets(buff,BUFFSZ,fp); + N = ams::localstrlen(buff); + if((N-1)>=0&&(buff[N-1]=='\n')) + { + buff[N-1] = '\0'; + N = N - 1; + } + if((N-1)>=0&&(buff[N-1]=='\r')) + { + buff[N-1] = '\0'; + N = N-1; + } + N = ams::localstrlen(buff); + *s = buff; //s->copy(buff); + } + else + { + s->resize(0); + } + }//null guards + return; + } + + void freadlines(FILE *fp, std::vector *lines) + { + amsstring ln; + //note - this does not resize the lines to zero, just adds to them + if(fp!=NULL && lines!=NULL) + { + while(!feof(fp)) + { + freadline(fp,&ln); + lines->push_back(ln); + } + } + return; + } + + void fwritelines(FILE *fp, amsstring *s) + { + if(fp!=NULL && s!=NULL) + { + fputs(s->cstring,fp); + } + return; + } + + void fwritelines(FILE *fp, std::vector *lines) + { + int I; + if(fp!=NULL && lines!=NULL) + { + for(I=0;Isize();I++) + { + fputs(lines->at(I).cstring,fp); + if(Isize()-1) fputs("\n",fp); + } + } + return; + } + + void freadtxtfile(FILE *fp, amsstring *s) + { + amsstring q; + if(fp!=NULL&&s!=NULL) + { + while(!feof(fp)) + { + freadline(fp,&q); + if(!feof(fp)) + { + q.append("\n"); + s->append(q); + } + } + } + return; + } + + amsstring amsstring::operator+(const amsstring &other) + { + amsstring ret; + amsstring q1 = *this; + amsstring q2 = other; + ret = ""; + ret.append(q1); + ret.append(q2); + return ret; + } + + const amsstring amsstring::operator+(const amsstring &other) const + { + amsstring ret; + amsstring q1 = *this; + amsstring q2 = other; + ret = ""; + ret.append(q1); + ret.append(q2); + return ret; + } + + amsstring amsstring::operator+(const ams_chartype *other) + { + amsstring ret; + amsstring q1 = *this; + ret = ""; + ret.append(q1); + ret.append(other); + return ret; + } + + const amsstring amsstring::operator+(const ams_chartype *other) const + { + amsstring ret; + amsstring q1 = *this; + ret = ""; + ret.append(q1); + ret.append(other); + return ret; + } + + amsstring amsstring::operator+(const ams_chartype other) + { + amsstring ret; + amsstring q1 = *this; + ret = ""; + ret.append(q1); + ret.append(other); + return ret; + } + + const amsstring amsstring::operator+(const ams_chartype other) const + { + amsstring ret; + amsstring q1 = *this; + ret = ""; + ret.append(q1); + ret.append(other); + return ret; + } + + +}; \ No newline at end of file diff --git a/src/amsstring4/amsstring4_portability.cpp b/src/amsstring4/amsstring4_portability.cpp new file mode 100644 index 0000000..aa00a94 --- /dev/null +++ b/src/amsstring4/amsstring4_portability.cpp @@ -0,0 +1,177 @@ +#include + +namespace ams +{ + +//snprintf, vsnprintf should now be part of the C++ standard library +//as of C++11, so I don't think I need quite as elaborate a compatibility +//shim as in the previous library. +//It *should* compile with MinGW and Visual Studio. + + +//src must be a NULL terminated string, or have more indices than the size of the destination buffer +//I'm seeing platform specific memory leaks in the strncpy and strcpy_s implementations in valgrind +//This is a dirt simple function, I shouldn't *need* to depend on a platform specific implementation, +//... so, writing my own. +// +//This function copies the string src to dest. +//It stops when either size-1 characters have been copied to +//dest, or a null terminator has been encountered in src. +// +//The return value is the number of characters copied, excluding the null terminator +//or an error code. +// +//All additional positions in dest are padded with null terminators. Size is intended to be the size +//of the dest buffer. +int amsstrcpy_s(char *dest, int size, const char *src) +{ + int ret = 0; + int I=0; + char c='\0'; + + if(dest==NULL) + { + ret = -2; + return ret; + } + + if(src==NULL) + { + ret = -1; + return ret; + } + + for(I=0;I0) +// { +// dest[size-1] = '\0'; +// } +// #elif defined(__MINGW32__) || defined(__MINGW64__) || defined(_WIN32) +// //use strcpy_s +// //ret = (int)strcpy_s(dest,size,src); +// strcpy_s(dest,size,src); +// ret = 0; +// if(size>0) +// { +// dest[size-1] = '\0'; +// } +// #else +// #pragma message("amsstrcpy_s: Unsupported architecture - neither linux nor mingw64 nor msvc") +// #endif +// } +// else +// { +// if(size>0) +// { +// dest[0] = '\0'; +// ret = -2; //src was NULL +// } +// } +// } +// else +// { +// ret = -1; //dest was a null pointer +// } + +// return ret; +// } + + +//sprintf_s +//snprintf +// +int amssprintf_s(char *s, int n, const char *format, ...) +{ + int ret = 0; + + va_list args; + va_start(args, format); + //exampleV(b, args); + //va_arg(val,datatype) + if(s!=NULL) + { + #if defined(LINUX) || defined(linux) || defined(__linux__) || defined(__GNUC__) + //use snprintf + ret = (int)vsnprintf(s,n,format,args); + s[n-1] = '\0'; + + #elif defined(__MINGW32__) || defined(__MINGW64__) || defined(_WIN32) + //use sprintf_s + ret = (int)vsprintf_s(s,n,format,args); + #else + #pragma message("amssprintf_s: Unsupported architecture - neither linux nor mingw64 nor msvc") + #endif + } + + va_end(args); + return ret; +} + +//Using the C library's sscanf function is more robust +//than atod or atof. It returns valid numbers for infs and nans +//Returns nan for any uninterpretable string +double amsstrtonum(const char *str) +{ + int q; + double ret = std::numeric_limits::quiet_NaN(); + try + { + //sscanf(s.cstring,"%lf",&ret); + //stod(const std::string& str, std::size_t* pos) + //calls std::strtod + //strtod(const char *, &ptr) + //wcstrtod + //ret = atof(str); //old c-style parser + q = sscanf(str,"%lf",&ret); + if(q==0) + { + ret = std::numeric_limits::quiet_NaN(); + } + } + catch(int e) + { + ret = std::numeric_limits::quiet_NaN(); + } + return ret; +} + + + +}; \ No newline at end of file diff --git a/src/amsstring4/amsstring4_tests1.cpp b/src/amsstring4/amsstring4_tests1.cpp new file mode 100644 index 0000000..2d78542 --- /dev/null +++ b/src/amsstring4/amsstring4_tests1.cpp @@ -0,0 +1,475 @@ +#include + +namespace ams +{ + + void amsstring3_basic_string_test1() + { + char q1,c; + unsigned char q2; + uint8_t q3; + int I; + + printf("Basic string tests1.\n"); + for(I=-127;I<256;I++) + { + c = (char)I; + q2 = (unsigned char) I; + //printf("I=%d %d %c %02x %02x %02x\n",I,(int)c,c,c,(unsigned char)c,I); + printf("I=%d, %c, %02x\n",I,q2,q2); + } + + q2 = (unsigned char) '\r'; + printf("\nLF: %c After LF %c After LF2 \n",q2,q2); + } + + void amsstring3_sscanf_test1() + { + char buf[500]; + double d; + int q; + + // //vsnprintf(buf,500," -123.456E10 "); + // snprintf(buf,500," -123.456E10 "); + // //sprintf_s(buf,500," -123.456E10 "); + // q=sscanf(buf,"%lf",&d); + // printf("Buffer %s reads as %1.4g, q=%d\n",buf,d,q); + + // snprintf(buf,500," -inf "); + // //sprintf_s(buf,500," -123.456E10 "); + // q=sscanf(buf,"%lf",&d); + // printf("Buffer %s reads as %1.4g, q=%d\n",buf,d,q); + + // snprintf(buf,500,"3"); + // //sprintf_s(buf,500," -123.456E10 "); + // q=sscanf(buf,"%lf",&d); + // printf("Buffer %s reads as %1.4g, q=%d\n",buf,d,q); + + // snprintf(buf,500," #.QUAN0 "); + // //sprintf_s(buf,500," -123.456E10 "); + // q=sscanf(buf,"%lf",&d); + // printf("Buffer %s reads as %1.4g, q=%d\n",buf,d,q); + + // snprintf(buf,500,"nan"); + // //sprintf_s(buf,500," -123.456E10 "); + // q=sscanf(buf,"%lf",&d); + // printf("Buffer %s reads as %1.4g, q=%d\n",buf,d,q); + + amssprintf_s(buf,500,"-3"); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + + amssprintf_s(buf,500," -3 "); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + + amssprintf_s(buf,500,"\t\t-3\t\n "); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + + amssprintf_s(buf,500," +3E+1 "); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + + amssprintf_s(buf,500,"2,3,4"); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + + amssprintf_s(buf,500,"inf"); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + + amssprintf_s(buf,500,"-inf"); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + + amssprintf_s(buf,500,"nan"); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + + amssprintf_s(buf,500,"1.0*4E3"); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + + amssprintf_s(buf,2,"2,3,4"); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + + amssprintf_s(buf,2,NULL); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + + amssprintf_s(NULL,2,"100"); + d = amsstrtonum(buf); + printf("String %s reads as %1.4g\n",buf,d); + } + + void amsstring3_basic_string_test2() + { + amsstring s1,s2; + //const amsstring s3; //don't do this - just accept that strings must be mutable + + s1="Hello world"; + s2 = s1; + printf("%d %c\n",(ams_chartype) '\0', (ams_chartype) '\0'); + printf("s1: '%s', s2: '%s'\n",s1.cstring,s2.cstring); + int I; + for(I=-5;Is2:'%s'?:%d\n",s1.cstring,s2.cstring,s1>s2); + s1 = "hello"; s2 = "Hello"; + printf("s1:'%s's2:'%s'?:%d\n",s1.cstring,s2.cstring,s1>s2); + + //s3 = "Hello constant world."; + //printf("s3= '%s'\n",s3.cstring); + + } + + void amsstring3_memoryleakcheck1() + { + amsstring q1,q2,q3; + int I; + + q1.sprintf(1000,"%1.100g,",ams::pi); + printf("q1='%s'\n",q1.cstring); + printf("q1.size()=%d\n",q1.size()); + q2 = q1; + for(I=0;I<100;I++) + { + q1.substring(0,q1.length-1,&q1); + printf("q1 = substr; q1='%s'\n",q1.cstring); + } + + q1 = q2; + q1.resize(10000000); + for(I=0;I<20;I++) + { + printf("resize test %d\n",(int)I); + q2 = q1; + q2.resize(10000000); + q3 = q2; + q3.resize(10000000); + q1 = q3; + q1.resize(10000000); + } + return; + } + + void amsstring3_memoryleakcheck2() + { + int I; + amsstring q1; + + for(I=0;I<100;I++) + { + q1.sprintf(4,"%1.100g",ams::pi); + } + printf("q1=%s\n",q1.cstring); + } + + void amsstring3_stringtests2() + { + amsstring q1,q2; + int I; + amsarray qarr; + + q1.insert(0,"Hello world"); + printf("q1='%s'\n",q1.cstring); + + for(I=-2;I<15;I++) + { + q2 = q1; + q2.insert(I,""); + printf("q2=q1;q2.insert(%d,') = '%s' size=%d\n",I,q2.cstring,q2.size()); + } + + q1 = "Hello world."; + for(I=-5;I<15;I++) + { + q2 = q1; + q2.remove(I); + printf("q2.remove(%d) = '%s'\n",I,q2.cstring); + } + + for(I=-5;I<15;I++) + { + q2 = q1; + q2.remove(I,I+2); + printf("q2.remove(%d,%d) = '%s'\n",I,I+2,q2.cstring); + } + + q1 = "Hello hEllo 1,2,3;"; + printf("q1='%s'\n",q1.cstring); + q1.tolower(); + printf("q1='%s'\n",q1.cstring); + q1.toupper(); + printf("q1='%s'\n",q1.cstring); + + q1.append("hello more appened stuff..."); + printf("q1='%s'\n",q1.cstring); + + q1.substring(-5,5,&q1); + printf("q1='%s'\n",q1.cstring); + + q1 = "Hello 1,2,3"; + printf("q1='%s', q1.isvalidnumber() = %d, q1.strtonum=%1.6g\n",q1.cstring,q1.isvalidnumber(),q1.strtonum()); + q1 = " 3.1415 "; + printf("q1='%s', q1.isvalidnumber() = %d, q1.strtonum=%1.6g\n",q1.cstring,q1.isvalidnumber(),q1.strtonum()); + q1 = "-inf"; + printf("q1='%s', q1.isvalidnumber() = %d, q1.strtonum=%1.6g\n",q1.cstring,q1.isvalidnumber(),q1.strtonum()); + q1 = "nan"; + printf("q1='%s', q1.isvalidnumber() = %d, q1.strtonum=%1.6g\n",q1.cstring,q1.isvalidnumber(),q1.strtonum()); + q1 = "1.1E1,2.2E2"; + printf("q1='%s', q1.isvalidnumber() = %d, q1.strtonum=%1.6g\n",q1.cstring,q1.isvalidnumber(),q1.strtonum()); + q1 = ",1,2,3"; + printf("q1='%s', q1.isvalidnumber() = %d, q1.strtonum=%1.6g\n",q1.cstring,q1.isvalidnumber(),q1.strtonum()); + + // qarr.resize(1000); + // for(I=0;I<1000;I++) + // { + // qarr[I].sprintf(1000,"%1.500g\n",ams::pi); + // qarr[I].resize(1000000); + // } + // printf("%d",qarr[0].size()); + q1 = "Hello world"; + for(I=-2;I<15;I++) + { + q1.substring(I,I+3,&q2); + printf("q1[%d:%d] = '%s' size=%d\n",I,I+3,q2.cstring,q2.size()); + } + + return; + } + + void amsstring3_test_find() + { + amsstring q1,q2,q3; + int I; + + q1 = "hello world"; + q3 = ""); + printf("q2=q1;q2.insert(%d,') = '%s' q2.find(q3)=%d\n",I,q2.cstring,q2.find(q3,0,0)); + } + + q3 = ""; + printf("\nq3='%s'\n",q3.cstring); + for(I=-1;I<13;I++) + { + q2 = q1; + q2.insert(I,""); + printf("q2=q1;q2.insert(%d,') = '%s' q2.find(q3)=%d\n",I,q2.cstring,q2.find(q3,0,0)); + } + + q3 = " "; + printf("\nq3='%s'\n",q3.cstring); + for(I=-1;I<13;I++) + { + q2 = q1; + q2.insert(I,""); + printf("q2=q1;q2.insert(%d,') = '%s' q2.find(q3)=%d\n",I,q2.cstring,q2.find(q3,0,0)); + } + + return; + } + + void amsstring3_test_splitlines() + { + int I; + amsstring q1; + amsarray lns; + std::vector lns2; + q1 = "This is a \n string on \n multiple \r\n lines\n\n with CR\\LFs\n"; + //q1 = "\n\n"; + //q1 = ""; + //q1 = "More malformed\r string nonsense\n\r\n\r\r\na"; + + printf("q1='%s'\n",q1.cstring); + splitlines(&q1,&lns2); + for(I=0;I strs; + int I; + + q1 = "this is a string to split "; + printf("string='%s'\n",q1.cstring); + split(&q1," ",&strs); + for(I=0;I strs; + int I; + + q1 = " "; + printf("string ws ='%s'\n",q1.cstring); + stripwhitespace(&q1); + printf("string nows='%s'\n",q1.cstring); + + q1 = "\t something = something else\t "; + printf("string ws ='%s'\n",q1.cstring); + stripwhitespace(&q1); + printf("string nows='%s'\n",q1.cstring); + + stripwhitespace(NULL); + + q1 = "\t something = something else\t "; + printf("string ws ='%s'\n",q1.cstring); + stripallwhitespace(&q1); + printf("string allws='%s'\n",q1.cstring); + + return; + } + + void amsstring3_test_freadwrite() + { + FILE *fp = NULL; + FILE *fp2 = NULL; + int I; + amsstring q; + std::vector q2; + + fp = fopen("../ref/0p375_hexbolt.scad","r"); + fp2 = fopen("../ref/testrewrite.scad","w+"); + + I = 0; + while(!feof(fp)) + { + freadline(fp,&q); + printf("Line %d: '%s'\n",I,q.cstring); + I = I + 1; + } + + fseek(fp,SEEK_SET,0); + + freadlines(fp,&q2); + for(I=0;I + +namespace ams +{ + + + //UC codepoints + //0x00 to 0x10FFFF (~24 bits, with the remainder being escape sequences and the like) + + //different processors order bytes differently (endianness) + + //UTF-8 + // 0x00 - 0x7F: 1 byte + // 0x00: U+0000 - only when representing the null character + + //21 bit values + // 0b0xxxxxxx 0x00000000 0x0000007F + // 0b110xxxxx 01xxxxxx 0x00000080 0x000007FF + // 0b1110xxxx 01xxxxxx 01xxxxxx 0x00000800 0x0000FFFF + // 0b11110xxx 01xxxxxx 01xxxxxx 01xxxxxx 0x00010000 0x0010FFFF + + + static void _intl_print_ui32bits(uint32_t q) + { + int I; + + for(I=32-1;I>=24;I--) + { + if((q & 1<=16;I--) + { + if((q & 1<=8;I--) + { + if((q & 1<=0;I--) + { + if((q & 1<=0;I--) + { + if((q & 1< &codepoints) + { + long I,J; + ams_chartype c0; + uint32_t cp0,cpw; + + int escmode; + int escs; + + int correct = 1; + + codepoints.reserve(str.length); + escmode = 0; + escs = 0; + for(I=0;I0) + { + if((c0 & 0b11000000)==0b10000000) + { + cpw = (uint32_t)(c0 & 0b00111111); + cp0 = cp0 + (cpw<<((uint32_t)6*(escs-1))); + escs--; + //printf("debug: escs=%d cp0=",escs); _intl_print_ui32bits(cp0); printf("\n"); + } + else + { + //invalid escape character - this is bad UTF-8 + correct = 0; + escs = 0; + escmode = 0; + //don't append anything + //printf("incorrect!\n"); + } + } + + if(escmode!=0 && escs==0) + { + //end escape mode, push character, return to mode 0; + escmode = 0; + escs = 0; + codepoints.append(cp0); + } + + } //for chars in string + + codepoints.shrink_to_fit(); + + return correct; + } + + int string_to_uccodepoints(const amsstring *str, amsarray *codepoints) + { + long I,J; + ams_chartype c0; + uint32_t cp0,cpw; + + int escmode; + int escs; + + int correct = 1; + + codepoints->reserve(str->length); + escmode = 0; + escs = 0; + for(I=0;Ilength+1;I++) + { + c0 = str->cstring[I]; + if(c0=='\0') + { + //end of string, terminate search + cp0 = 0; + codepoints->append(cp0); + break; + } + + else if(escmode==0 && (c0 & 0b10000000)==0) + { + //normal ASCII character + cp0 = (uint32_t)((unsigned char)c0); + codepoints->append(cp0); + } + else if(escmode==0 && (c0 & 0b11100000)==0b11000000) + { + escmode = 1; escs = 1; + cp0 = 0; + cpw = (uint32_t)(c0 & 0b00011111); + cp0 = cp0 + (cpw<<((uint32_t)6)); + //printf("debug: escs=%d cp0=",escs); _intl_print_ui32bits(cp0); printf("\n"); + } + else if(escmode==0 && (c0 & 0b11110000)==0b11100000) + { + escmode = 2; escs = 2; + cp0 = 0; + cpw = (uint32_t)(c0 & 0b00001111); + cp0 = cp0 + (cpw<<((uint32_t)12)); + //printf("debug: escs=%d cp0=",escs); _intl_print_ui32bits(cp0); printf("\n"); + } + else if(escmode==0 && (c0 & 0b11111000)==0b11110000) + { + escmode = 3; escs = 3; + cp0 = 0; + cpw = (uint32_t)(c0 & 0b00000111); + cp0 = cp0 + (cpw<<((uint32_t)18)); + //printf("debug: escs=%d cp0=",escs); _intl_print_ui32bits(cp0); printf("\n"); + } + + else if(escmode!=0 && escs>0) + { + if((c0 & 0b11000000)==0b10000000) + { + cpw = (uint32_t)(c0 & 0b00111111); + cp0 = cp0 + (cpw<<((uint32_t)6*(escs-1))); + escs--; + //printf("debug: escs=%d cp0=",escs); _intl_print_ui32bits(cp0); printf("\n"); + } + else + { + //invalid escape character - this is bad UTF-8 + correct = 0; + escs = 0; + escmode = 0; + //don't append anything + //printf("incorrect!\n"); + } + } + + if(escmode!=0 && escs==0) + { + //end escape mode, push character, return to mode 0; + escmode = 0; + escs = 0; + codepoints->append(cp0); + } + + } //for chars in string + + codepoints->shrink_to_fit(); + + return correct; + } + + void uccodepoints_to_string(const amsarray &codepoints, amsstring &str) + { + long I,J; + uint8_t ch; + uint32_t cp,bits1,bits2,bits3,bits4; + + str.resize(codepoints.length*4+1); + str.cstring[str.length]='\0'; //guard against last char not being \0 + + J = 0; + for(I=0;I0 && cp<=0x0000007F) + { + ch = (uint8_t) cp; + str.cstring[J] = ((ams_chartype)((unsigned char)ch)); + J++; + } + + if(cp>=0x00000080 && cp<=0x000007FF) + { + bits1 = (cp & (0b00111111))+0b10000000; + bits2 = ((cp & (0b00011111<<6L))>>6)+0b11000000; + + //string.cstring[J] = ((ams_chartype)((unsigned char)bits2)); + str.cstring[J] = bits2; + J++; + //string.cstring[J] = ((ams_chartype)((unsigned char)bits1)); + str.cstring[J] = bits1; + J++; + } + + if(cp>=0x00000800 && cp<=0x0000FFFF) + { + bits1 = (cp & (0b00111111))+0b10000000; + bits2 = ((cp & (0b00111111<<6))>>6)+0b10000000; + bits3 = ((cp & (0b00001111<<12))>>12)+0b11100000; + + str.cstring[J] = ((ams_chartype)((unsigned char)bits3)); + J++; + str.cstring[J] = ((ams_chartype)((unsigned char)bits2)); + J++; + str.cstring[J] = ((ams_chartype)((unsigned char)bits1)); + J++; + } + + if(cp>=0x00010000 && cp<=0x010FFFFF) + { + bits1 = (cp & (0b00111111))+0b10000000; + bits2 = ((cp & (0b00111111<<6))>>6)+0b10000000; + bits3 = ((cp & (0b00111111<<12))>>12)+0b10000000; + bits4 = ((cp & (0b00000111<<18))>>18)+0b11110000; + + str.cstring[J] = ((ams_chartype)((unsigned char)bits4)); + J++; + str.cstring[J] = ((ams_chartype)((unsigned char)bits3)); + J++; + str.cstring[J] = ((ams_chartype)((unsigned char)bits2)); + J++; + str.cstring[J] = ((ams_chartype)((unsigned char)bits1)); + J++; + } + + } + + str.shrinktofit(); + return; + } + + void uccodepoints_to_string(const amsarray *codepoints, amsstring *str) + { + long I,J; + uint8_t ch; + uint32_t cp,bits1,bits2,bits3,bits4; + + str->resize(codepoints->length*4+1); + str->cstring[str->length]='\0'; //guard against last char not being \0 + + J = 0; + for(I=0;Ilength;I++) + { + cp = codepoints->at(I); + if(cp==0) + { + str->cstring[J] = '\0'; + J++; + break; + } + + //21 bit values + // 0b0xxxxxxx 0x00000000 0x0000007F + // 0b110xxxxx 01xxxxxx 0x00000080 0x000007FF + // 0b1110xxxx 01xxxxxx 01xxxxxx 0x00000800 0x0000FFFF + // 0b11110xxx 01xxxxxx 01xxxxxx 01xxxxxx 0x00010000 0x0010FFFF + + if(cp>0 && cp<=0x0000007F) + { + ch = (uint8_t) cp; + str->cstring[J] = ((ams_chartype)((unsigned char)ch)); + J++; + } + + if(cp>=0x00000080 && cp<=0x000007FF) + { + bits1 = (cp & (0b00111111))+0b10000000; + bits2 = ((cp & (0b00011111<<6L))>>6)+0b11000000; + + //string.cstring[J] = ((ams_chartype)((unsigned char)bits2)); + str->cstring[J] = bits2; + J++; + //string.cstring[J] = ((ams_chartype)((unsigned char)bits1)); + str->cstring[J] = bits1; + J++; + } + + if(cp>=0x00000800 && cp<=0x0000FFFF) + { + bits1 = (cp & (0b00111111))+0b10000000; + bits2 = ((cp & (0b00111111<<6))>>6)+0b10000000; + bits3 = ((cp & (0b00001111<<12))>>12)+0b11100000; + + str->cstring[J] = ((ams_chartype)((unsigned char)bits3)); + J++; + str->cstring[J] = ((ams_chartype)((unsigned char)bits2)); + J++; + str->cstring[J] = ((ams_chartype)((unsigned char)bits1)); + J++; + } + + if(cp>=0x00010000 && cp<=0x010FFFFF) + { + bits1 = (cp & (0b00111111))+0b10000000; + bits2 = ((cp & (0b00111111<<6))>>6)+0b10000000; + bits3 = ((cp & (0b00111111<<12))>>12)+0b10000000; + bits4 = ((cp & (0b00000111<<18))>>18)+0b11110000; + + str->cstring[J] = ((ams_chartype)((unsigned char)bits4)); + J++; + str->cstring[J] = ((ams_chartype)((unsigned char)bits3)); + J++; + str->cstring[J] = ((ams_chartype)((unsigned char)bits2)); + J++; + str->cstring[J] = ((ams_chartype)((unsigned char)bits1)); + J++; + } + + } + + str->shrinktofit(); + return; + } + + + void test_unicode_ascii_int_conv() + { + int I; + char c; + unsigned char uc; + int J1,J2; + + for(I=0;I<255;I++) + { + c = (char)I; + uc = (unsigned char) c; + J1 = (int)c; + J2 = (int)uc; + + printf("%d\t%c\t%c\t%d\t%d\n",I,c,uc,J1,J2); + } + + + return; + } + + static void test_unicode_conv1_sub(uint32_t codept) + { + long I; + amsarray codepts1; + amsarray codepts2; + amsstring s1,s2; + + codepts1.resize(1); + codepts1[0] = codept; + + uccodepoints_to_string(codepts1,s1); + + printf("UCC: "); _intl_print_ui32bits(codepts1[0]); printf("\n"); + printf("STR: "); + for(I=0;I *cp) + { + int I; + int pass = 1; + amsstring s1,s2; + amsarray cp2; + + uccodepoints_to_string(cp,&s1); + string_to_uccodepoints(&s1,&cp2); + uccodepoints_to_string(&cp2,&s2); + + if(s1==s2 && *cp==cp2) + { + pass = 1; + } + else + { + pass = 0; + printf("FAIL: \n"); + + } + + + return pass; + } + + static void test_gen_rand_codepts(int len, amsarray *cp) + { + long I; + cp->resize(len+1); + for(I=0;Iat(I) = ams::randi(1,0x0010FFFF); + } + cp->at(len) = 0; + + return; + } + + void test_unicode_conv2() + { + long I,J; + int pass; + amsarray cp; + + long ntests = 10000; + long passes = 0; + long failures = 0; + + int len = 30; + + printf("Testing unicode to string conversion.\n"); + printf("%ld tests of %d random codepoints each.\n",ntests,len); + + for(I=0;I