copypaste
parent
87ccfdf279
commit
eb9ae4316f
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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<uint8_t> *bytes, amsstring *str);
|
||||
|
||||
|
||||
|
||||
int base64decode(amsstring *str, ams::amsarray<uint8_t> *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<uint8_t> *bytes);
|
||||
|
||||
int base64decode_strict(amsstring *str, ams::amsarray<uint8_t> *bytes);
|
||||
|
||||
void test_base64encode();
|
||||
void test_base64encode_fuzztest();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,22 @@
|
||||
#ifndef __AMSSTRING4_UNICODE_HPP__
|
||||
#define __AMSSTRING4_UNICODE_HPP__
|
||||
|
||||
namespace ams
|
||||
{
|
||||
|
||||
int string_to_uccodepoints(const amsstring &str, amsarray<uint32_t> &codepoints);
|
||||
int string_to_uccodepoints(const amsstring *str, amsarray<uint32_t> *codepoints);
|
||||
|
||||
void uccodepoints_to_string(const amsarray<uint32_t> &codepoints, amsstring &str);
|
||||
void uccodepoints_to_string(const amsarray<uint32_t> *codepoints, amsstring *str);
|
||||
|
||||
void test_unicode_ascii_int_conv();
|
||||
void test_unicode_conv1();
|
||||
|
||||
void test_unicode_conv2();
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,565 @@
|
||||
#include <amsstring4/amsstring4.hpp>
|
||||
|
||||
namespace ams
|
||||
{
|
||||
|
||||
//PGP / GPG text armor, binary encoding scheme:
|
||||
//
|
||||
|
||||
//HTML embedded image file binary encoding scheme:
|
||||
//example:
|
||||
// <img alt="Embedded Image" width="158" height="158"
|
||||
// src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJ4A..." />
|
||||
// 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
|
||||
// <link rel="stylesheet" type="text/css"
|
||||
// href="data:text/css;base64,LyogKioqKiogVGVtcGxhdGUgKioq..." />
|
||||
// (X)HTML Javascript Embedding Example
|
||||
// <script type="text/javascript"
|
||||
// src="data:text/javascript;base64,dmFyIHNjT2JqMSA9IG5ldyBzY3Jv..."></script>
|
||||
|
||||
//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<uint8_t> *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(I<bytes->length)
|
||||
{
|
||||
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;I<str->length;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<uint8_t> *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;I<str->length && fail==0;I++)
|
||||
while(I<str->length && 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)<str->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<uint8_t> *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;I<str->length;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<uint8_t> *b)
|
||||
{
|
||||
long I;
|
||||
b->resize(s->length);
|
||||
for(I=0;I<s->length;I++)
|
||||
{
|
||||
b->at(I) = (unsigned char) s->cstring[I];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void _intl_convbs(amsarray<uint8_t> *b,amsstring *s)
|
||||
{
|
||||
long I;
|
||||
s->resize(b->length);
|
||||
for(I=0;I<b->length;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<uint8_t> 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<b1.length;I++)
|
||||
// printf("%d:",b1.at(I));
|
||||
// printf("\n");
|
||||
|
||||
printf("Original: '%s'\n",s1.cstring);
|
||||
printf("Encoded: '%s'\n",s1e.cstring);
|
||||
printf("Decoded: '%s', ret=%d\n",s2.cstring,ret);
|
||||
|
||||
|
||||
s1 = "light work";
|
||||
_intl_convsb(&s1,&b1);
|
||||
base64encode(&b1,&s1e);
|
||||
ret = base64decode(&s1e,&b2,bstrict);
|
||||
_intl_convbs(&b2,&s2);
|
||||
|
||||
// for(I=0;I<b1.length;I++)
|
||||
// printf("%d:",b1.at(I));
|
||||
// printf("\n");
|
||||
|
||||
printf("Original: '%s'\n",s1.cstring);
|
||||
printf("Encoded: '%s'\n",s1e.cstring);
|
||||
printf("Decoded: '%s', ret=%d\n",s2.cstring,ret);
|
||||
|
||||
s1 = "light wor";
|
||||
_intl_convsb(&s1,&b1);
|
||||
base64encode(&b1,&s1e);
|
||||
s1e.insert(3,"\n");
|
||||
s1e.insert(5,"\t");
|
||||
s1e.insert(7,"}");
|
||||
//s1e.insert(1,"}");
|
||||
ret = base64decode(&s1e,&b2,bstrict);
|
||||
//ret = base64decode_strict(&s1e,&b2);
|
||||
_intl_convbs(&b2,&s2);
|
||||
|
||||
// for(I=0;I<b1.length;I++)
|
||||
// printf("%d:",b1.at(I));
|
||||
// printf("\n");
|
||||
|
||||
printf("Original: '%s'\n",s1.cstring);
|
||||
printf("Encoded: '%s'\n",s1e.cstring);
|
||||
printf("Decoded: '%s', ret=%d\n",s2.cstring,ret);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void test_base64encode_fuzztest()
|
||||
{
|
||||
long I,J;
|
||||
ams::amsarray<uint8_t> 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.size();J++)
|
||||
{
|
||||
bytes.data[J] = randd()*255;
|
||||
}
|
||||
base64encode(&bytes,&str);
|
||||
base64decode(&str,&bytes2,1);
|
||||
base64encode(&bytes2,&str2);
|
||||
if(bytes==bytes2 && str==str2)
|
||||
{
|
||||
passed++;
|
||||
}
|
||||
else
|
||||
{
|
||||
failed++;
|
||||
}
|
||||
|
||||
}
|
||||
printf("passed: %d, failed %d\n",passed,failed);
|
||||
return;
|
||||
}
|
||||
|
||||
int base64decode(amsstring *str, ams::amsarray<uint8_t> *bytes, bool bstrict=0)
|
||||
{
|
||||
int ret = 0;
|
||||
if(bstrict==1)
|
||||
{
|
||||
ret = base64decode_strict(str,bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = base64decode_liberal(str,bytes);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,177 @@
|
||||
#include <amsstring4/amsstring4.hpp>
|
||||
|
||||
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;I<size-1;I++)
|
||||
{
|
||||
c = src[I];
|
||||
ret = I;
|
||||
|
||||
if(c=='\0')
|
||||
{
|
||||
dest[I] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
dest[I] = c;
|
||||
}
|
||||
|
||||
for(I=ret+1;I<size;I++)
|
||||
{
|
||||
dest[I] = '\0';
|
||||
}
|
||||
dest[size-1] = '\0';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
//wrapper for strcpy_s and strncpy which should be portable between gnu and microsoft C libraries
|
||||
//strcpy_s
|
||||
//strncpy
|
||||
// int amsstrcpy_s(char *dest, int size, const char *src)
|
||||
// {
|
||||
// int ret = 0;
|
||||
// if(dest!=NULL)
|
||||
// {
|
||||
// if(src!=NULL)
|
||||
// {
|
||||
// #if defined(LINUX) || defined(linux) || defined(__linux__) || defined(__GNUC__)
|
||||
// //use strncpy
|
||||
// strncpy(dest,src,size);
|
||||
// ret = 0;
|
||||
// if(size>0)
|
||||
// {
|
||||
// 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<double>::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<double>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
catch(int e)
|
||||
{
|
||||
ret = std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
@ -0,0 +1,475 @@
|
||||
#include <amsstring4/amsstring4.hpp>
|
||||
|
||||
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;I<s2.size()+5;I++)
|
||||
{
|
||||
printf("s2[%d]: %d, %c \n",I,s2[I],s2[I]);
|
||||
}
|
||||
|
||||
for(I=-5;I<s2.size()+5;I++)
|
||||
{
|
||||
s1[I] = 'a';
|
||||
};
|
||||
printf("s1 = %s\n",s1.cstring);
|
||||
for(I=-5;I<s2.size()+5;I++)
|
||||
{
|
||||
printf("s1[%d]: %d, %c \n",I,s1[I],s1[I]);
|
||||
}
|
||||
|
||||
printf("s1:'%s'==s2:'%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);
|
||||
s2.resize(15);
|
||||
s2[10] = 'b';
|
||||
//s2[5] = 'b';
|
||||
//s2[6] = 'b';
|
||||
printf("s1.size()=%d, s2.size()=%d\n",s1.size(),s2.size());
|
||||
printf("s1:'%s'==s2:'%s'?:%d\n",s1.cstring,s2.cstring,s1==s2);
|
||||
s2[10] = 'a';
|
||||
s2.shrinktofit();
|
||||
printf("s1.size()=%d, s2.size()=%d\n",s1.size(),s2.size());
|
||||
printf("s1:'%s'==s2:'%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);
|
||||
s1 = "hello"; s2 = "Hello";
|
||||
printf("s1:'%s'<s2:'%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<amsstring> qarr;
|
||||
|
||||
q1.insert(0,"Hello world");
|
||||
printf("q1='%s'\n",q1.cstring);
|
||||
|
||||
for(I=-2;I<15;I++)
|
||||
{
|
||||
q2 = q1;
|
||||
q2.insert(I,"<insert>");
|
||||
printf("q2=q1;q2.insert(%d,'<insert'>) = '%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 = "<inser";
|
||||
printf("q3='%s'\n",q3.cstring);
|
||||
for(I=-1;I<13;I++)
|
||||
{
|
||||
q2 = q1;
|
||||
q2.insert(I,"<insert>");
|
||||
printf("q2=q1;q2.insert(%d,'<insert'>) = '%s' q2.find(q3)=%d\n",I,q2.cstring,q2.find(q3,0,0));
|
||||
}
|
||||
|
||||
q3 = "<inSeRt>";
|
||||
printf("\nq3='%s'\n",q3.cstring);
|
||||
for(I=-1;I<13;I++)
|
||||
{
|
||||
q2 = q1;
|
||||
q2.insert(I,"<insert>");
|
||||
printf("q2=q1;q2.insert(%d,'<insert'>) = '%s' q2.find(q3)=%d\n",I,q2.cstring,q2.find(q3,0,0));
|
||||
}
|
||||
|
||||
q3 = "<insert> ";
|
||||
printf("\nq3='%s'\n",q3.cstring);
|
||||
for(I=-1;I<13;I++)
|
||||
{
|
||||
q2 = q1;
|
||||
q2.insert(I,"<insert>");
|
||||
printf("q2=q1;q2.insert(%d,'<insert'>) = '%s' q2.find(q3)=%d\n",I,q2.cstring,q2.find(q3,0,0));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void amsstring3_test_splitlines()
|
||||
{
|
||||
int I;
|
||||
amsstring q1;
|
||||
amsarray<amsstring> lns;
|
||||
std::vector<amsstring> 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<lns2.size();I++)
|
||||
{
|
||||
printf("Line %d: '%s'\n",I,lns2[I].cstring);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void amsstring3_test_split()
|
||||
{
|
||||
amsstring q1;
|
||||
std::vector<amsstring> strs;
|
||||
int I;
|
||||
|
||||
q1 = "this is a string to split ";
|
||||
printf("string='%s'\n",q1.cstring);
|
||||
split(&q1," ",&strs);
|
||||
for(I=0;I<strs.size();I++)
|
||||
{
|
||||
printf("S[%d]: '%s'\n",I,strs[I].cstring);
|
||||
}
|
||||
|
||||
q1 = "A\tbunch of tab\tseparated \tvariables\t";
|
||||
printf("string='%s'\n",q1.cstring);
|
||||
split(&q1,"\t",&strs);
|
||||
for(I=0;I<strs.size();I++)
|
||||
{
|
||||
printf("S[%d]: '%s'\n",I,strs[I].cstring);
|
||||
}
|
||||
|
||||
q1 = "Delimiter is abcd, a ab abcd qabcqdqabcdq";
|
||||
printf("string='%s'\n",q1.cstring);
|
||||
split(&q1,"abcd",&strs);
|
||||
for(I=0;I<strs.size();I++)
|
||||
{
|
||||
printf("S[%d]: '%s'\n",I,strs[I].cstring);
|
||||
}
|
||||
|
||||
q1 = "abc";
|
||||
printf("string='%s'\n",q1.cstring);
|
||||
split(&q1,"abcd",&strs);
|
||||
for(I=0;I<strs.size();I++)
|
||||
{
|
||||
printf("S[%d]: '%s'\n",I,strs[I].cstring);
|
||||
}
|
||||
|
||||
q1 = "";
|
||||
printf("string='%s'\n",q1.cstring);
|
||||
split(&q1,"abcd",&strs);
|
||||
for(I=0;I<strs.size();I++)
|
||||
{
|
||||
printf("S[%d]: '%s'\n",I,strs[I].cstring);
|
||||
}
|
||||
|
||||
q1 = "A string not to split.";
|
||||
printf("string='%s'\n",q1.cstring);
|
||||
split(&q1,"",&strs);
|
||||
for(I=0;I<strs.size();I++)
|
||||
{
|
||||
printf("S[%d]: '%s'\n",I,strs[I].cstring);
|
||||
}
|
||||
|
||||
q1 = "A string to split\tby\t \twhitespace";
|
||||
printf("string='%s'\n",q1.cstring);
|
||||
splitwhitespace(&q1,&strs);
|
||||
for(I=0;I<strs.size();I++)
|
||||
{
|
||||
printf("S[%d]: '%s'\n",I,strs[I].cstring);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void amsstring3_test_strip()
|
||||
{
|
||||
amsstring q1;
|
||||
std::vector<amsstring> 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<amsstring> 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<q2.size();I++)
|
||||
{
|
||||
printf("Line %d: '%s'\n",I,q2[I].cstring);
|
||||
}
|
||||
|
||||
//q = "This is a test file\nto write\n\thello\n\n";
|
||||
//fwritelines(fp2,&q);
|
||||
|
||||
fwritelines(fp2,&q2);
|
||||
|
||||
|
||||
fclose(fp);
|
||||
fclose(fp2);
|
||||
return;
|
||||
}
|
||||
|
||||
void amsstring3_test_concatenation_operators()
|
||||
{
|
||||
ams::amsstring a,b,c,d;
|
||||
|
||||
a = "";
|
||||
a = a + "hello";
|
||||
a = a+ " world\n";
|
||||
b = a+a;
|
||||
c = b+a;
|
||||
|
||||
printf("%s\n",b.cstring);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
@ -0,0 +1,638 @@
|
||||
#include <amsstring4/amsstring4.hpp>
|
||||
|
||||
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<<I)!=0)
|
||||
{
|
||||
printf("1");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("0");
|
||||
}
|
||||
}
|
||||
printf(" ");
|
||||
for(I=24-1;I>=16;I--)
|
||||
{
|
||||
if((q & 1<<I)!=0)
|
||||
{
|
||||
printf("1");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("0");
|
||||
}
|
||||
}
|
||||
printf(" ");
|
||||
for(I=16-1;I>=8;I--)
|
||||
{
|
||||
if((q & 1<<I)!=0)
|
||||
{
|
||||
printf("1");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("0");
|
||||
}
|
||||
}
|
||||
printf(" ");
|
||||
for(I=8-1;I>=0;I--)
|
||||
{
|
||||
if((q & 1<<I)!=0)
|
||||
{
|
||||
printf("1");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("0");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void _intl_print_ui8bits(uint8_t q)
|
||||
{
|
||||
int I;
|
||||
for(I=8-1;I>=0;I--)
|
||||
{
|
||||
if((q & 1<<I)!=0)
|
||||
{
|
||||
printf("1");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("0");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int string_to_uccodepoints(const amsstring &str, amsarray<uint32_t> &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;I<str.length+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;
|
||||
}
|
||||
|
||||
int string_to_uccodepoints(const amsstring *str, amsarray<uint32_t> *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;I<str->length+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<uint32_t> &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;I<codepoints.length;I++)
|
||||
{
|
||||
cp = codepoints[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 uccodepoints_to_string(const amsarray<uint32_t> *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;I<codepoints->length;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<uint32_t> codepts1;
|
||||
amsarray<uint32_t> 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<s1.length;I++)
|
||||
{
|
||||
_intl_print_ui8bits((uint8_t)(unsigned char)s1.cstring[I]);
|
||||
printf(" ");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
string_to_uccodepoints(s1,codepts2);
|
||||
printf("UCC: "); _intl_print_ui32bits(codepts2[0]); printf("\n");
|
||||
|
||||
uccodepoints_to_string(codepts2,s2);
|
||||
printf("STR: ");
|
||||
for(I=0;I<s2.length;I++)
|
||||
{
|
||||
_intl_print_ui8bits(s2.cstring[I]);
|
||||
printf(" ");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
if(s1==s2)
|
||||
{
|
||||
printf("STR: PASS\t");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("STR: FAIL\t");
|
||||
}
|
||||
if(codepts1[0]==codepts2[0])
|
||||
{
|
||||
printf("UCC: PASS\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("UCC: FAIL\n");
|
||||
}
|
||||
|
||||
//printf("Can I print the char to terminal?: %s\n",s1.cstring);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void test_unicode_conv1()
|
||||
{
|
||||
uint32_t codept;
|
||||
|
||||
//Test bounding cases
|
||||
|
||||
codept = 0x7F;
|
||||
test_unicode_conv1_sub(codept);
|
||||
printf("\n\n");
|
||||
|
||||
codept = 0x07FF;
|
||||
test_unicode_conv1_sub(codept);
|
||||
printf("\n\n");
|
||||
|
||||
codept = 0xFFFF;
|
||||
test_unicode_conv1_sub(codept);
|
||||
printf("\n\n");
|
||||
|
||||
codept = 0x0010FFFF;
|
||||
test_unicode_conv1_sub(codept);
|
||||
printf("\n\n");
|
||||
|
||||
codept = 0x07FFFFFF;
|
||||
test_unicode_conv1_sub(codept);
|
||||
printf("\n\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static int test_unicode_conv2_sub(amsarray<uint32_t> *cp)
|
||||
{
|
||||
int I;
|
||||
int pass = 1;
|
||||
amsstring s1,s2;
|
||||
amsarray<uint32_t> 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<uint32_t> *cp)
|
||||
{
|
||||
long I;
|
||||
cp->resize(len+1);
|
||||
for(I=0;I<len;I++)
|
||||
{
|
||||
cp->at(I) = ams::randi(1,0x0010FFFF);
|
||||
}
|
||||
cp->at(len) = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void test_unicode_conv2()
|
||||
{
|
||||
long I,J;
|
||||
int pass;
|
||||
amsarray<uint32_t> 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<ntests;I++)
|
||||
{
|
||||
test_gen_rand_codepts(10,&cp);
|
||||
if(I==0)
|
||||
{
|
||||
printf("ex cp string:");
|
||||
for(J=0;J<cp.length;J++)
|
||||
{
|
||||
printf("%d,",cp[J]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
pass = test_unicode_conv2_sub(&cp);
|
||||
if(pass==1)
|
||||
{
|
||||
passes++;
|
||||
}
|
||||
else
|
||||
{
|
||||
failures++;
|
||||
}
|
||||
|
||||
if(I%(ntests/10)==0)
|
||||
{
|
||||
printf("Test %ld....\n",I);
|
||||
}
|
||||
}
|
||||
|
||||
printf("%ld tests, %ld passes, %ld failures.\n",ntests,passes,failures);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
};
|
Loading…
Reference in New Issue