1 /*
2 *
3 * C++ Portable Types Library (PTypes)
4 * Version 2.1.1 Released 27-Jun-2007
5 *
6 * Copyright (C) 2001-2007 Hovik Melikyan
7 *
8 * http://www.melikyan.com/ptypes/
9 *
10 */
11 #include <stdlib.h>
12 #include <stdio.h>
13
14 #include "pport.h"
15 #include "ptypes.h"
16 #include "pstreams.h"
17 #include "pinet.h"
18 #include "ptime.h"
19
20
21 #ifndef PTYPES_ST
22 #include "pasync.h"
23 #endif
24
25
26 USING_PTYPES
27
28
passert(bool cond)29 void passert(bool cond)
30 {
31 if (!cond)
32 {
33 fatal(0xa3, "*** ASSERTION FAILED ***");
34 }
35 }
36
37
showstr(const char * shouldbe,const char * is)38 void showstr(const char* shouldbe, const char* is)
39 {
40 passert(strcmp(shouldbe, is) == 0);
41 pout.putf("[%s] %s\n", shouldbe, is);
42 }
43
44
showstr(const char * shouldbe,const string & is)45 void showstr(const char* shouldbe, const string& is)
46 {
47 showstr(shouldbe, pconst(is));
48 }
49
50
showhex(const char * shouldbe,const char * is,int islen)51 void showhex(const char* shouldbe, const char* is, int islen)
52 {
53 string s;
54 int i;
55 for (i = 0; i < islen; i++)
56 s += itostring((unsigned char)is[i], 16, 2);
57 s = lowercase(s);
58 showstr(shouldbe, s);
59 }
60
61
showint(int shouldbe,int is)62 void showint(int shouldbe, int is)
63 {
64 passert(shouldbe == is);
65 pout.putf("[%d] %d\n", shouldbe, is);
66 }
67
68
showint(large shouldbe,large is)69 void showint(large shouldbe, large is)
70 {
71 passert(shouldbe == is);
72 pout.putf("[%lld] %lld\n", shouldbe, is);
73 }
74
75
showchar(char shouldbe,char is)76 void showchar(char shouldbe, char is)
77 {
78 passert(shouldbe == is);
79 pout.putf("[%c] %c\n", shouldbe, is);
80 }
81
82
83 //
84 // string test
85 //
86
87
const_string_call(const string & s)88 void const_string_call(const string& s)
89 {
90 showchar('R', s[2]);
91 }
92
93
string_test1()94 void string_test1()
95 {
96 pout.put("\n--- STRING CLASS\n");
97
98 static char strbuf[10] = "STRING";
99
100 char c = 'A';
101
102 string s1 = "string";
103 s1 = s1;
104 s1 += s1;
105 del(s1, 6, 6);
106 string s2 = s1;
107 string s3;
108 string s4(strbuf, strlen(strbuf));
109 string s5 = 'A';
110 string s6 = c;
111
112 showstr("string", s1);
113 showstr(s1, s2);
114 showstr("", s3);
115 showstr("STRING", s4);
116 const_string_call(s4);
117 showchar('I', s4[3]);
118 showint(6, length(s4));
119 showint(2, refcount(s1));
120 clear(s2);
121 showint(1, refcount(s1));
122 s2 = s1;
123 unique(s1);
124 showint(1, refcount(s1));
125 setlength(s1, 64);
126 string s7 = s1;
127 setlength(s1, 3);
128 showstr("str", s1);
129 showstr("strING", s1 += copy(s4, 3, 3));
130 del(s1, 3, 3);
131 showstr("str", s1);
132 ins("ing", s1, 0);
133 showstr("ingstr", s1);
134 s2 = "str" + s1 + "ing";
135 showstr("stringstring", s2);
136 s3 = s2;
137 s3 = "strungstrung";
138 s3 = s2;
139 s3 = "strung";
140 del(s3, 4);
141 showstr("stru", s3);
142
143 s2 = "str" + s1;
144 s2 = s1 + "str";
145 s2 += "str";
146
147 s2 = 's' + s1;
148 s2 = s1 + 's';
149 s2 += 's';
150
151 s2 = c + s1;
152 s2 = s1 + c;
153 s2 += c;
154
155 s2 = 'a';
156 s2 = c;
157 }
158
159
string_test2()160 void string_test2()
161 {
162 pout.put("\n--- STRING CLASS\n");
163
164 string s1 = "ingstr";
165 string s2 = "stringstring";
166 string s4 = "STRING";
167
168 showint(1, pos('t', s2));
169 showint(2, pos("ri", s2));
170 showint(3, pos(s1, s2));
171 showint(1, contains("tr", s2, 1));
172 showint(0, contains("tr", s2, 2));
173 showint(1, s4 == "STRING");
174 showint(1, "STRING" == s4);
175 showchar('R', s4[2]);
176
177 string s5 = 'A';
178 showint(1, s5 == 'A');
179 showint(1, 'A' == s5);
180
181 showstr("123456789", itostring(123456789));
182 showstr("123", itostring(char(123)));
183 showstr("-000123", itostring(-123, 10, 7, '0'));
184 showstr("0ABCDE", itostring(0xabcde, 16, 6));
185 showstr("-9223372036854775808", itostring(LARGE_MIN));
186 showstr("18446744073709551615", itostring(ULARGE_MAX));
187
188 showint(1234, (int)stringtoi("1234"));
189 showint(large(0x15AF), stringtoue("15AF", 16));
190 showint(LARGE_MAX, stringtoue("5zzzzzzzzzz", 64));
191
192 try
193 {
194 // out of range by 1
195 stringtoue("18446744073709551616", 10);
196 fatal(0xb0, "Conversion overflow not detected");
197 }
198 catch (econv* e)
199 {
200 showstr("Out of range: '18446744073709551616'", e->get_message());
201 delete e;
202 }
203
204 showint(large(123), stringtoie("123"));
205 showint(large(-123), stringtoie("-123"));
206 showint(LARGE_MIN, stringtoie("-9223372036854775808"));
207 showint(LARGE_MAX, stringtoie("9223372036854775807"));
208
209 try
210 {
211 // out of range by 1
212 stringtoie("9223372036854775808");
213 fatal(0xb0, "Conversion overflow not detected");
214 }
215 catch (econv* e)
216 {
217 showstr("Out of range: '9223372036854775808'", e->get_message());
218 delete e;
219 }
220
221 showstr("abcabc", lowercase(s1 = "aBCAbc"));
222 showstr("abcabc", lowercase(s1));
223 }
224
225
string_benchmarks()226 void string_benchmarks()
227 {
228 pout.put("\n--- STRING BENCHMARKS\n");
229
230 int i;
231 string s1 = "string";
232 string s2;
233
234 // for the first time we run the test to let the VM settle down and stop swapping
235 for (i = 0; i < 156000; i++)
236 s2 += s1;
237
238 // here is the actual test
239 clear(s2);
240 datetime start = now();
241 for (i = 0; i < 156000; i++)
242 s2 += s1;
243 datetime diff = now() - start;
244
245 pout.putf("Performance index compared to WinNT on P3/800MHz (smaller = better):\n%.3f\n", diff / 1000.0);
246 }
247
248
249 //
250 // cset test
251 //
252
cset_test()253 void cset_test()
254 {
255 pout.put("\n--- CSET CLASS\n");
256
257 cset s1 = "~09~0a~0d~F0 A-Z~~";
258 cset s2 = "A-F";
259 char c = 'B';
260
261 showstr("~09~0a~0d A-Z~~~f0", asstring(s1));
262 s1 -= char(0xf0);
263 s1 -= cset("~00-~20");
264 showstr("A-Z~~", asstring(s1));
265 s1 -= s2;
266 showstr("G-Z~~", asstring(s1));
267 s1 += s2;
268 s1 += ' ';
269 showstr(" A-Z~~", asstring(s1));
270 showint(1, s2 == cset("A-F"));
271 showint(1, s2 <= s1);
272 s1 -= 'A';
273 s1 -= c;
274 showint(0, s2 <= s1);
275 s1 = s1 + char(0xf1);
276 showint(1, char(0xf1) & s1);
277 showint(0, char(0xf2) & s1);
278 }
279
280
281 //
282 // podlist
283 //
284
285
const_podlist_test(const tpodlist<int,true> & p)286 void const_podlist_test(const tpodlist<int, true>& p)
287 {
288 // int& i = p[0];
289 showint(7, p[1]);
290 }
291
292
podlist_test()293 void podlist_test()
294 {
295 pout.put("\n--- PODLIST\n");
296
297 tpodlist<int, true> p;
298 p.add() = 6;
299 p.add(8);
300 p.ins(1, 7);
301 showint(7, p[1]);
302 const_podlist_test(p);
303 showint(3, p.get_count());
304 p.set_count(5);
305 p.ins(5) = 10;
306 showint(10, p.top());
307 p.pop();
308
309 tpodlist<int, true> p1;
310 p1.add(p);
311 p1.pop();
312 p1.add(p);
313 }
314
315
316 //
317 // ptrlist
318 //
319
320
321 struct known: public unknown
322 {
323 int value;
knownknown324 known(int ivalue): value(ivalue) {}
325 };
326
327 typedef tobjlist<known> knownlist;
328
329
ol_asstring(const knownlist & s)330 string ol_asstring(const knownlist& s)
331 {
332 string ret = "{";
333 for (int i = 0; i < s.get_count(); i++) {
334 if (i > 0)
335 concat(ret, ", ");
336 ret += itostring(s[i]->value);
337 }
338 concat(ret, '}');
339 return ret;
340 }
341
342
ptrlist_test()343 void ptrlist_test()
344 {
345 pout.put("\n--- PTRLIST\n");
346
347 knownlist s1(true);
348 known* obj;
349
350 s1.add(new known(10));
351 s1.ins(0, new known(5));
352 s1.ins(2, obj = new known(20));
353 s1.add(new known(30));
354 s1.add(new known(40));
355 s1.put(4, new known(45));
356 s1.del(4);
357 s1.del(1);
358
359 s1[0];
360 showint(3, s1.get_count());
361 showint(1, s1.indexof(obj));
362 showstr("{5, 20, 30}", ol_asstring(s1));
363
364 s1.clear();
365 showstr("{}", ol_asstring(s1));
366 }
367
368
369
370 //
371 // strlist
372 //
373
374
375 typedef tstrlist<known> knownstrlist;
376
377
sl_asstring(const knownstrlist & s)378 string sl_asstring(const knownstrlist& s)
379 {
380 string ret = "{";
381 for (int i = 0; i < s.get_count(); i++)
382 {
383 if (i > 0)
384 concat(ret, ", ");
385 ret += s.getkey(i) + ":" + itostring(s[i]->value);
386 }
387 concat(ret, '}');
388 return ret;
389 }
390
391
strlist_test()392 void strlist_test()
393 {
394 pout.put("\n--- STRLIST\n");
395
396 knownstrlist s1(SL_OWNOBJECTS);
397 known* obj;
398
399 s1.add("ten", new known(10));
400 s1.ins(0, "five", new known(5));
401 s1.ins(2, "twenty", obj = new known(20));
402 s1.add("thirty", new known(30));
403 s1.add("forty", new known(40));
404 s1.put(4, "forty five", new known(45));
405 s1.del(4);
406 s1.del(1);
407
408 showint(3, s1.get_count());
409 showint(1, s1.indexof(obj));
410 showint(2, s1.indexof("thirty"));
411 showint(2, s1.indexof("THIRTY"));
412 showint(-1, s1.indexof("forty"));
413
414 showstr("{five:5, twenty:20, thirty:30}", sl_asstring(s1));
415
416 knownstrlist s2(SL_OWNOBJECTS | SL_SORTED | SL_CASESENS);
417 s2.add("five", new known(5));
418 s2.add("ten", new known(10));
419 s2.add("twenty", new known(20));
420 s2.add("thirty", new known(30));
421 s2.add("forty", new known(40));
422
423 showint(5, s2.get_count());
424 showint(3, s2.indexof("thirty"));
425 showint(-1, s2.indexof("THIRTY"));
426 showint(-1, s2.indexof("hovik"));
427
428 showstr("{five:5, forty:40, ten:10, thirty:30, twenty:20}", sl_asstring(s2));
429
430 s2.clear();
431 showstr("{}", sl_asstring(s2));
432
433 tstrlist<known> s3(SL_OWNOBJECTS | SL_SORTED | SL_DUPLICATES);
434
435 s3.add("a", nil);
436 s3.add("b", nil);
437 s3.add("b", nil);
438 s3.add("b", nil);
439 s3.add("b", nil);
440 s3.add("b", nil);
441 s3.add("b", nil);
442 s3.add("c", nil);
443
444 showint(1, s3.indexof("b"));
445 s3.del(1, 2);
446
447 tstrlist<known> s(SL_OWNOBJECTS | SL_SORTED);
448
449 s.put("five", new known(5));
450 s.put("ten", new known(10));
451 s.put("twenty", new known(20));
452 s.put("thirty", new known(30));
453 s.put("forty", new known(40));
454
455 showint(20, s["twenty"]->value);
456 showint(0, pintptr(s["hovik"]));
457 showint(5, s.get_count());
458 s.put("twenty", nil);
459 showint(4, s.get_count());
460 }
461
462
463 //
464 // textmap
465 //
466
467
textmap_test()468 void textmap_test()
469 {
470 pout.put("\n--- TEXTMAP CLASS\n");
471
472 textmap c;
473
474 c.put("name1", "value1");
475 c.put("name2", "value2");
476 c.put("name1", "value3");
477 showstr("name2", c.getkey(1));
478 c.put("name2", "");
479 showint(1, c.get_count());
480 showstr("value3", c["name1"]);
481 showstr("", c["name2"]);
482 }
483
484
485
486 //
487 // streams
488 //
489
490
491 char buf1[] = "This is a test.";
492 char buf2[] = "The file should contain readable text.";
493
494 const char* fname = "stmtest.txt";
495
outfile_test()496 void outfile_test()
497 {
498 pout.put("\n--- OUTFILE CLASS\n");
499
500 showint(8, sizeof(off_t));
501
502 outfile f(fname, false);
503 f.set_umode(0600);
504 f.set_bufsize(3);
505
506 f.open();
507 f.put(buf1[0]);
508 f.put(buf1[1]);
509 f.put("is is a TEST.");
510 f.seek(-5, IO_END);
511 f.put("tes*/");
512 f.seek(13);
513 f.put("t.");
514 f.puteol();
515 f.close();
516
517 f.set_append(true);
518 f.open();
519 f.write(buf2, strlen(buf2));
520 f.puteol();
521 f.close();
522
523 pnull.put("This should go to nowhere I");
524 pnull.put("This should go to nowhere II");
525 }
526
527
infile_test()528 void infile_test()
529 {
530 pout.put("\n--- INFILE CLASS\n");
531
532 compref<instm> f = &pin;
533 f = new infile(fname);
534 f->set_bufsize(3);
535
536 char temp[4];
537
538 f->open();
539 pout.putf("%c", f->get());
540 pout.putf("%s\n", pconst(f->line()));
541 f->read(temp, sizeof temp - 1);
542 temp[sizeof temp - 1] = 0;
543 pout.putf("%s", temp);
544 f->get();
545 f->putback();
546 pout.putf("%s", pconst(f->token(cset("~20-~FF"))));
547 f->preview();
548 if (f->get_eol())
549 {
550 f->skipline();
551 pout.put("\n");
552 }
553 if (f->get_eof())
554 pout.put("EOF\n");
555 // f.error(1, "Test error message");
556
557 }
558
559
mem_test()560 void mem_test()
561 {
562 pout.put("\n--- OUT/IN MEMORY CLASS\n");
563
564 {
565 outmemory m(12);
566 m.open();
567 m.put("MEMOry");
568 m.put(" c");
569 m.put("lass is working");
570 m.seek(1);
571 m.put("emo");
572 showstr("Memory class", m.get_strdata());
573 // try reuse
574 m.open();
575 m.put("memory");
576 showstr("memory", m.get_strdata());
577 }
578 {
579 inmemory m("");
580 m.open();
581 showstr("", m.token("*"));
582 m.set_strdata("gArbaGe");
583 m.open();
584 // try reuse
585 m.set_strdata("string strong");
586 m.set_bufsize(2); // has no effect
587 m.open();
588 showstr("string", m.token("a-z"));
589 m.seek(-6, IO_END);
590 showstr("strong", m.token("a-z"));
591 }
592 }
593
594
595 #ifndef PTYPES_ST
596
597 //
598 // multithreading
599 //
600
601 //
602 // rwlock test
603 //
604
605 const int rw_max_threads = 30;
606 const int rw_max_tries = 30;
607 const int rw_max_delay = 20;
608 const int rw_rw_ratio = 5;
609 const bool rw_swap = false;
610
611
612 class rwthread: public thread
613 {
614 protected:
615 virtual void execute();
616 public:
rwthread()617 rwthread(): thread(false) {}
~rwthread()618 virtual ~rwthread() { waitfor(); }
619 };
620
621
622 rwlock rw;
623
624 int reader_cnt = 0;
625 int writer_cnt = 0;
626 int total_writers = 0;
627 int total_readers = 0;
628 int max_readers = 0;
629
630
prand(int max)631 int prand(int max)
632 {
633 return rand() % max;
634 }
635
636
execute()637 void rwthread::execute()
638 {
639
640 for(int i = 0; i < rw_max_tries; i++)
641 {
642 psleep(prand(rw_max_delay));
643 bool writer = prand(rw_rw_ratio) == 0;
644 if (writer ^ rw_swap)
645 {
646 rw.wrlock();
647 pout.put('w');
648 if (pincrement(&writer_cnt) > 1)
649 fatal(0xa0, "Writer: Huh?! Writers in here?");
650 pincrement(&total_writers);
651 }
652 else
653 {
654 rw.rdlock();
655 pout.put('.');
656 int t;
657 if ((t = pincrement(&reader_cnt)) > max_readers)
658 max_readers = t;
659 if (writer_cnt > 0)
660 fatal(0xa1, "Reader: Huh?! Writers in here?");
661 pincrement(&total_readers);
662 }
663 psleep(prand(rw_max_delay));
664 if (writer ^ rw_swap)
665 pdecrement(&writer_cnt);
666 else
667 pdecrement(&reader_cnt);
668 rw.unlock();
669 }
670 }
671
672
673
rwlock_test()674 void rwlock_test()
675 {
676 // #ifdef __PTYPES_RWLOCK__
677 pout.put("\n--- RWLOCK\n");
678
679 rwthread* threads[rw_max_threads];
680
681 srand((unsigned)time(0));
682
683 int i;
684 for(i = 0; i < rw_max_threads; i++)
685 {
686 threads[i] = new rwthread();
687 threads[i]->start();
688 }
689 for(i = 0; i < rw_max_threads; i++)
690 delete threads[i];
691
692 pout.putf("\nmax readers: %d\n", max_readers);
693 pout.putline("do writers 'starve'?");
694 // #endif
695 }
696
697
698 //
699 // jobqueue test ----------------------------------------------------------
700 //
701
702
703 const int MSG_MYJOB = MSG_USER + 1;
704 const int NUM_JOB_THREADS = 3;
705
706
707 class jobthread: public thread
708 {
709 protected:
710 int id;
711 jobqueue* jq;
712 virtual void execute();
713 public:
jobthread(int iid,jobqueue * ijq)714 jobthread(int iid, jobqueue* ijq): thread(false), id(iid), jq(ijq) {}
~jobthread()715 ~jobthread() { waitfor(); }
716 };
717
718
execute()719 void jobthread::execute()
720 {
721 bool quit = false;
722 while (!quit)
723 {
724 message* m = jq->getmessage();
725 try
726 {
727 switch (m->id)
728 {
729 case MSG_MYJOB:
730 // ... do the job ...
731 psleep(prand(10));
732 // report
733 pout.putf("Thread %d finished the job (param=%d)\n", id, m->param);
734 break;
735 case MSG_QUIT:
736 quit = true;
737 break;
738 }
739 }
740 catch(...)
741 {
742 // the message object must be freed!
743 delete m;
744 throw;
745 }
746 delete m;
747 }
748 }
749
750
jobqueue_test()751 void jobqueue_test()
752 {
753 pout.put("\n--- JOBQUEUE\n");
754
755 jobqueue jq(3);
756 tobjlist<jobthread> threads(true);
757
758 srand((unsigned)time(0));
759
760 // create the thread pool and start all threads
761 int i;
762 for(i = 0; i < NUM_JOB_THREADS; i++)
763 {
764 jobthread* j = new jobthread(i + 1, &jq);
765 j->start();
766 threads.add(j);
767 }
768
769 // post jobs for processing
770 jq.post(MSG_MYJOB, 1);
771 jq.post(MSG_MYJOB, 2);
772 jq.post(MSG_MYJOB, 3);
773 jq.post(MSG_MYJOB, 4);
774 jq.post(MSG_MYJOB, 5);
775 jq.post(MSG_MYJOB, 6);
776 jq.post(MSG_MYJOB, 7);
777 jq.post(MSG_MYJOB, 8);
778
779 // terminate all threads
780 for(i = 0; i < NUM_JOB_THREADS; i++)
781 jq.post(MSG_QUIT);
782
783 // threads are being waitfor()'ed and destroyed
784 // automatically by the list object
785 }
786
787
788
789 //
790 // msgqueue test ----------------------------------------------------------
791 //
792
793
794 const int MSG_DIAG = MSG_USER + 1;
795
796
797 //
798 // msgqueue test
799 //
800
801 //
802 // class diagmessage
803 //
804
805 class diagmessage: public message
806 {
807 protected:
808 string module;
809 string diagstr;
810 friend class diagthread;
811 public:
diagmessage(string imodule,string idiagstr)812 diagmessage(string imodule, string idiagstr)
813 : message(MSG_DIAG), module(imodule),
814 diagstr(idiagstr) {}
815 };
816
817
818 //
819 // class diagthread
820 //
821
822 class diagthread: public thread, protected msgqueue
823 {
824 protected:
825 virtual void execute(); // override thread::execute()
826 virtual void cleanup(); // override thread::cleanup()
827 virtual void msghandler(message& msg); // override msgqueue::msghandler()
828 public:
diagthread()829 diagthread(): thread(false), msgqueue() { }
830 void postdiag(string module, string diagstr);
831 void postquit();
832 };
833
834
postdiag(string module,string diagstr)835 void diagthread::postdiag(string module, string diagstr)
836 {
837 msgqueue::post(new diagmessage(module, diagstr));
838 }
839
840
postquit()841 void diagthread::postquit()
842 {
843 msgqueue::post(MSG_QUIT);
844 }
845
846
execute()847 void diagthread::execute()
848 {
849 // starts message queue processing; calls
850 // msghandler for each message
851 msgqueue::run();
852 }
853
854
cleanup()855 void diagthread::cleanup()
856 {
857 }
858
859
msghandler(message & msg)860 void diagthread::msghandler(message& msg)
861 {
862 switch (msg.id)
863 {
864 case MSG_DIAG:
865 {
866 diagmessage& m = (diagmessage&)msg;
867 pout.putf("%s: %s\n", pconst(m.module), pconst(m.diagstr));
868 }
869 break;
870 default:
871 defhandler(msg);
872 }
873 }
874
875
876 //
877 // class testthread
878 //
879
880
881 class testthread: public thread
882 {
883 protected:
884 diagthread* diag;
885 string myname;
886 virtual void execute();
887 virtual void cleanup();
888 public:
889 semaphore sem;
890 timedsem tsem;
testthread(diagthread * idiag)891 testthread(diagthread* idiag)
892 : thread(false), diag(idiag), myname("testthread"), sem(0), tsem(0) {}
893 };
894
895
execute()896 void testthread::execute()
897 {
898 diag->postdiag(myname, "starts and enters sleep for 1 second");
899 psleep(1000);
900 diag->postdiag(myname, "signals the timed semaphore");
901 tsem.post();
902 diag->postdiag(myname, "releases the simple semaphore");
903 sem.post();
904 diag->postdiag(myname, "enters sleep for 1 more second");
905 psleep(1000);
906 }
907
908
cleanup()909 void testthread::cleanup()
910 {
911 diag->postdiag(myname, "terminates");
912 }
913
914
thread_test()915 int thread_test()
916 {
917 pout.put("\n--- THREAD AND SEMAPHORE CLASSES\n");
918
919 int v = 0;
920 showint(0, pexchange(&v, 5));
921 showint(5, pexchange(&v, 10));
922
923 void* pv = 0;
924 passert(pexchange(&pv, &v) == 0);
925 passert(pexchange(&pv, 0) == &v);
926
927 showint(11, pincrement(&v));
928 showint(10, pdecrement(&v));
929
930 diagthread diag;
931 testthread thr(&diag);
932
933 string myname = "main";
934
935 diag.start();
936 thr.start();
937
938 diag.postdiag(myname, "waits 5 secs for the timed semaphore (actually wakes up after a second)");
939 thr.tsem.wait(5000); // must exit after 1 second instead of 5
940
941 diag.postdiag(myname, "waits for the semaphore");
942 thr.sem.wait();
943 diag.postdiag(myname, "now waits for testthread to terminate");
944 thr.waitfor();
945
946 diag.postquit();
947 diag.waitfor();
948 return 0;
949 }
950
951
952 //
953 // trigger test
954 //
955
956
957 class trigthread: public thread
958 {
959 protected:
960 diagthread* diag;
961 string myname;
962 virtual void execute();
963 public:
964 trigger trig;
trigthread(diagthread * idiag)965 trigthread(diagthread* idiag)
966 : thread(false), diag(idiag), myname("trigthread"), trig(true, false) {}
~trigthread()967 virtual ~trigthread() { waitfor(); }
968 };
969
970
execute()971 void trigthread::execute()
972 {
973 diag->postdiag(myname, "waits on the trigger");
974 trig.wait();
975
976 psleep(2000);
977 diag->postdiag(myname, "waits on the trigger");
978 trig.wait();
979
980 diag->postdiag(myname, "terminates");
981 }
982
983
trigger_test()984 int trigger_test()
985 {
986 pout.put("\n--- TRIGGER\n");
987
988 diagthread diag;
989 trigthread thr(&diag);
990
991 string myname = "main";
992
993 diag.start();
994 thr.start();
995
996 psleep(1000);
997 diag.postdiag(myname, "posts the trigger");
998 thr.trig.post();
999
1000 psleep(1000);
1001 diag.postdiag(myname, "posts the trigger again");
1002 thr.trig.post();
1003
1004 thr.waitfor();
1005 diag.postquit();
1006 diag.waitfor();
1007 return 0;
1008 }
1009
1010
1011
1012 #endif // PTYPES_ST
1013
1014
1015 //
1016 // md5 test
1017 //
1018
1019 static md5_digest digest;
1020
md5str(string data)1021 char* md5str(string data)
1022 {
1023 outmd5 m;
1024 m.open();
1025 m.put(data);
1026 memcpy(digest, m.get_bindigest(), sizeof(md5_digest));
1027 return (char*)digest;
1028 }
1029
cryptpw(string username,string password)1030 string cryptpw(string username, string password)
1031 {
1032 outmd5 m;
1033 m.open();
1034 m.put(username);
1035 m.put(password);
1036 m.close();
1037 return m.get_digest();
1038 }
1039
md5_test()1040 void md5_test()
1041 {
1042 pout.put("\n--- MD5 OUTPUT STREAM\n");
1043 // MD5 test suite from RFC1321
1044 showhex("d41d8cd98f00b204e9800998ecf8427e", md5str(""), md5_digsize);
1045 showhex("0cc175b9c0f1b6a831c399e269772661", md5str("a"), md5_digsize);
1046 showhex("900150983cd24fb0d6963f7d28e17f72", md5str("abc"), md5_digsize);
1047 showhex("f96b697d7cb7938d525a2f31aaf161d0", md5str("message digest"), md5_digsize);
1048 showhex("c3fcd3d76192e4007dfb496cca67e13b", md5str("abcdefghijklmnopqrstuvwxyz"), md5_digsize);
1049 showhex("d174ab98d277d9f5a5611c2c9f419d9f", md5str("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), md5_digsize);
1050 showhex("57edf4a22be3c955ac49da2e2107b67a", md5str("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), md5_digsize);
1051
1052 showstr("t0htL.C9vunX8SPPsJjDmk", cryptpw("hovik", "myfavoritelonglonglongpassword"));
1053 }
1054
1055
1056 //
1057 // outstm::putf() test
1058 //
1059
putf_test()1060 void putf_test()
1061 {
1062 pout.put("\n--- PUTF TEST\n");
1063 outmemory m;
1064 m.open();
1065
1066 m.putf("%s, %c, %d, %llx", "string", 'A', 1234, large(-1));
1067 showstr("string, A, 1234, ffffffffffffffff", m.get_strdata());
1068
1069 m.open();
1070
1071 m.putf(" %%, %#o, %+010d", 0765, -3);
1072 showstr(" %, 0765, -000000003", m.get_strdata());
1073 }
1074
1075
1076 //
1077 // pinet/socket tests
1078 //
1079
1080
inet_test1()1081 void inet_test1()
1082 {
1083 try
1084 {
1085 pout.put("\n--- INET SOCKET & UTILITIES\n");
1086
1087 ipaddress ip(127, 0, 0, 1);
1088
1089 // as a test target host we use the one that would never be down! :)
1090 string testname = "www.apache.org";
1091
1092 ip = phostbyname(testname);
1093 string ips = iptostring(ip);
1094 pout.putf("IP address of %s: %s\n", pconst(testname), (ip == ipnone) ? "failed" : pconst(ips));
1095
1096 if (ip != ipnone)
1097 {
1098 string hs = phostbyaddr(ip);
1099 pout.putf("Name of %s: %s\n", pconst(ips), pconst(hs));
1100 }
1101
1102 pout.putf("Canonical name of your local www: %s\n", pconst(phostcname("www")));
1103
1104 testname = "www.melikyan.com";
1105 pout.putf("\nTrying %s:80...\n", pconst(testname));
1106 ipstream s(testname, 80);
1107
1108 const char* request =
1109 "GET /ptypes/test.txt HTTP/1.1\r\n"
1110 "Accept: * /*\r\n"
1111 "User-Agent: ptypes_test/2.1\r\n"
1112 "Host: www.melikyan.com\r\n"
1113 "Connection: close\r\n\r\n";
1114
1115 s.open();
1116 s.put(request);
1117 s.flush();
1118
1119 while (!s.get_eof())
1120 {
1121 char buf[16];
1122 int r = s.read(buf, sizeof(buf));
1123 pout.write(buf, r);
1124 }
1125 pout.put("\n");
1126
1127 s.close();
1128 }
1129 catch(estream* e)
1130 {
1131 perr.putf("Socket error: %s\n", pconst(e->get_message()));
1132 delete e;
1133 }
1134 }
1135
1136
1137 #ifndef PTYPES_ST
1138
1139
1140 const int testport = 8085;
1141
1142
1143 class svthread: public thread, protected ipstmserver
1144 {
1145 protected:
1146 virtual void execute(); // override thread::execute()
1147 virtual void cleanup(); // override thread::cleanup()
1148 public:
svthread()1149 svthread(): thread(false) {}
1150 virtual ~svthread();
1151 };
1152
1153
~svthread()1154 svthread::~svthread()
1155 {
1156 waitfor();
1157 }
1158
1159
execute()1160 void svthread::execute()
1161 {
1162 ipstream client; // a socket object to communicate with the client
1163
1164 try
1165 {
1166 bindall(testport); // listen to all local addresses on port 8081
1167 serve(client); // wait infinitely for a connection request
1168
1169 if (client.get_active())
1170 {
1171 // read one line from the stream; note that theoretically the line can be long,
1172 // so calling client.line(buf, sizeof(buf)) here is a much better idea
1173 string req = lowercase(client.line());
1174 if (req == "hello")
1175 {
1176 // try to reverse-lookup the client's IP
1177 string host = phostbyaddr(client.get_ip());
1178 if (isempty(host))
1179 host = iptostring(client.get_ip());
1180
1181 // now send our greeting to the client
1182 client.putf("Hello, %s (%a), nice to see you!\n",
1183 pconst(host), long(client.get_ip()));
1184 client.flush();
1185 }
1186
1187 // close() should be called explicitly to shut down the socket
1188 // *gracefully*; otherwise ipstream's destructor may close the
1189 // socket but in a less polite manner
1190 client.close();
1191 }
1192 }
1193 catch(estream* e)
1194 {
1195 perr.putf("Server error: %s\n", pconst(e->get_message()));
1196 delete e;
1197 }
1198
1199 // a real server could enter an infinite loop serving requests
1200 // and producing separate threads for each connection
1201 }
1202
1203
cleanup()1204 void svthread::cleanup()
1205 {
1206 }
1207
1208
inet_test2()1209 void inet_test2()
1210 {
1211 pout.put("\n--- INET CLIENT/SERVER\n");
1212
1213 // we run the server in a separate thread in order to be able
1214 // to imitate a client connection from the main thread
1215 svthread server;
1216
1217 pout.put("\nStarting the server thread...\n");
1218 server.start(); // it's that easy! :)
1219
1220 // sleep some time to let the server start its job
1221 psleep(1000);
1222
1223 try
1224 {
1225 // now create a client socket and send a greeting to our server
1226 ipstream client(ipaddress(127, 0, 0, 1), testport);
1227 client.open();
1228
1229 pout.put("Sending a request to the server...\n");
1230 client.putline("Hello");
1231 client.flush();
1232 string rsp = client.line();
1233 pout.putf("Received: %s\n", pconst(rsp));
1234 pout.putf("My address and port: %s:%d\n",
1235 pconst(iptostring(client.get_myip())), client.get_myport());
1236
1237 client.close();
1238 }
1239 catch(estream* e)
1240 {
1241 perr.putf("Error: %s\n", pconst(e->get_message()));
1242 delete e;
1243 }
1244
1245 }
1246
1247
1248 //
1249 // UDP test
1250 //
1251
1252
1253 class msgsvthread: public thread
1254 {
1255 protected:
1256 void execute();
1257 public:
msgsvthread()1258 msgsvthread(): thread(false) {}
~msgsvthread()1259 virtual ~msgsvthread() { waitfor(); }
1260 };
1261
1262
execute()1263 void msgsvthread::execute()
1264 {
1265 ipmsgserver s;
1266 s.bindall(testport);
1267 try
1268 {
1269 string req = s.receive(1024);
1270 pout.putf("Server received: %s\n", pconst(req));
1271 string rsp = "gotcha";
1272 s.send(rsp);
1273 }
1274 catch(estream* e)
1275 {
1276 perr.putf("Server error: %s\n", pconst(e->get_message()));
1277 delete e;
1278 }
1279 }
1280
1281
inet_test3()1282 void inet_test3()
1283 {
1284 pout.put("\n--- INET MESSAGE CLIENT/SERVER\n");
1285
1286 msgsvthread sv;
1287 sv.start();
1288 psleep(1000);
1289
1290 ipmessage m(ipbcast /* ipaddress(127, 0, 0, 1) */, testport);
1291 try
1292 {
1293 string msg = "hello";
1294 m.send(msg);
1295 string rsp = m.receive(1024);
1296 pout.putf("Client received: %s\n", pconst(rsp));
1297 }
1298 catch(estream* e)
1299 {
1300 perr.putf("Client error: %s\n", pconst(e->get_message()));
1301 delete e;
1302 }
1303 }
1304
1305
1306 //
1307 // named pipes test
1308 //
1309
1310
1311 #define TEST_PIPE "ptypes.test"
1312
1313
1314 class npthread: public thread, protected npserver
1315 {
1316 protected:
1317 virtual void execute();
1318 virtual void cleanup();
1319 public:
npthread()1320 npthread(): thread(false), npserver(TEST_PIPE) {}
1321 virtual ~npthread();
1322 };
1323
1324
~npthread()1325 npthread::~npthread()
1326 {
1327 waitfor();
1328 }
1329
1330
execute()1331 void npthread::execute()
1332 {
1333 namedpipe client;
1334
1335 try
1336 {
1337 serve(client);
1338
1339 if (client.get_active())
1340 {
1341 string req = lowercase(client.line());
1342 if (req == "hello")
1343 {
1344 client.putline("Hello, nice to see you!");
1345 client.flush();
1346 }
1347
1348 client.close();
1349 }
1350 }
1351 catch(estream* e)
1352 {
1353 perr.putf("Pipe server error: %s\n", pconst(e->get_message()));
1354 delete e;
1355 }
1356 }
1357
1358
cleanup()1359 void npthread::cleanup()
1360 {
1361 }
1362
1363
pipe_test()1364 void pipe_test()
1365 {
1366 npthread server;
1367
1368 pout.put("\n--- NAMED PIPES\n");
1369 pout.put("Starting the pipe server thread...\n");
1370 server.start();
1371
1372 psleep(1000);
1373
1374 namedpipe client(TEST_PIPE);
1375
1376 try
1377 {
1378 client.open();
1379
1380 pout.put("Sending a request to the server...\n");
1381 client.putline("Hello");
1382 client.flush();
1383 string rsp = client.line();
1384 pout.putf("Received: %s\n", pconst(rsp));
1385
1386 client.close();
1387 }
1388 catch(estream* e)
1389 {
1390 perr.putf("Error: %s\n", pconst(e->get_message()));
1391 delete e;
1392 }
1393 }
1394
1395
1396 #endif // PTYPES_ST
1397
1398
1399 //
1400 // date/time/calendar
1401 //
1402
1403
time_test()1404 void time_test()
1405 {
1406 pout.put("\n--- DATE/TIME/CALENDAR\n");
1407
1408 tzupdate();
1409
1410 int year, month, day;
1411 datetime d = encodedate(9999, 12, 31);
1412 decodedate(d, year, month, day);
1413 d = encodedate(1970, 1, 1);
1414 decodedate(d, year, month, day);
1415
1416 datetime dt;
1417 dt = invdatetime;
1418 int hour, min, sec, msec;
1419 dt = encodetime(23, 59, 59, 998);
1420 decodetime(dt, hour, min, sec, msec);
1421
1422 dt = encodedate(2001, 8, 27) + encodetime(14, 33, 10);
1423 d = encodedate(2001, 8, 28) + encodetime(14, 33, 10, 500);
1424 dayofweek(dt);
1425
1426 dt = now(false);
1427 pout.putf("Local time: %s\n", pconst(dttostring(dt, "%x %X %Z")));
1428 datetime utc = now();
1429 pout.putf("UTC time: %t GMT\n", utc);
1430
1431 time_t ut;
1432 time(&ut);
1433 pout.putline(dttostring(utodatetime(ut), "%c"));
1434
1435 int t = tzoffset();
1436 bool neg = t < 0;
1437 if (neg)
1438 t = -t;
1439 pout.putf("Time zone offset: %c%02d%02d\n", neg ? '-' : '+', t / 60, t % 60);
1440 {
1441 // PTypes' birthday (birth moment, if you wish)
1442 datetime d = encodedate(2000, 3, 30) + encodetime(13, 24, 58, 995);
1443 pout.putf("PTypes' birth moment: %T GMT\n", d);
1444
1445 // now see how old is PTypes in days, hours, minutes, etc
1446 datetime diff = now() - d;
1447 int hours, mins, secs, msecs;
1448 decodetime(diff, hours, mins, secs, msecs);
1449 pout.putf("PTypes' life time: %d days %d hours %d minutes %d seconds and %d milliseconds\n",
1450 days(diff), hours, mins, secs, msecs);
1451
1452 #ifndef PTYPES_ST
1453 // measure the difference in milliseconds between two calls to now()
1454 datetime m = now();
1455 psleep(17); // sleep for 17 milliseconds
1456 pout.putf("A 17 millisecond dream lasted actually %d milliseconds\n", int(now() - m));
1457 // this will show the actual precision of the clock on the given platform;
1458 // Windows, f.ex., always shows the difference in 10 msec increments
1459 #endif
1460 }
1461 }
1462
1463
1464 //
1465 // streams documentation example #2
1466 //
1467
1468
1469 const cset letters("_A-Za-z");
1470 const cset digits("0-9");
1471 const cset identchars = letters + digits;
1472 const cset otherchars = !letters;
1473
1474
doc_example()1475 void doc_example()
1476 {
1477 tstrlist<void*> dic(SL_SORTED);
1478
1479 infile f("../src/ptypes_test.cxx");
1480
1481 try
1482 {
1483 f.open();
1484
1485 while (!f.get_eof())
1486 {
1487 char c = f.preview();
1488
1489 // a C identifier starts with a letter
1490 if (c & letters)
1491 {
1492 // ... and may contain letters and digits
1493 string ident = f.token(identchars);
1494 int i;
1495 if (!dic.search(ident, i))
1496 dic.add(ident, 0);
1497 }
1498
1499 else
1500 f.skiptoken(otherchars);
1501 }
1502
1503 }
1504
1505 catch (estream* e)
1506 {
1507 pout.putf("Error: %s\n", pconst(e->get_message()));
1508 delete e;
1509 }
1510
1511 // now print the dictionary
1512 for (int i = 0; i < dic.get_count(); i++)
1513 pout.putline(dic.getkey(i));
1514 }
1515
1516
1517 //
1518 // variant
1519 //
1520
1521
variant_test()1522 void variant_test()
1523 {
1524 pout.put("\n--- VARIANT\n");
1525
1526 variant v0 = 'A';
1527 variant v1 = short(33);
1528 variant v2 = "456";
1529 variant v3 = int(v1) + int(v2);
1530 variant v4 = string(v1) + " cows";
1531 string s = string(v4);
1532 // s = v4;
1533 variant v5 = new component();
1534 variant v6;
1535
1536 put(v6, 291, v1);
1537 put(v6, "key", v2);
1538 put(v6, "another-key", "another-value");
1539 showstr("33 cows", string(v4));
1540 showint(456, v6["key"]);
1541 showstr("33", string(v1));
1542 showint(1, bool(v6));
1543 v2 = aclone(v6);
1544 v5 = v6;
1545
1546 variant vi;
1547 int i;
1548 for (i = 0; anext(v6, i, vi);)
1549 pout.putf("%d\n", int(vi));
1550
1551 variant v7;
1552 aadd(v7, v1);
1553 aadd(v7, v3);
1554 aadd(v7, v4);
1555 adel(v7, 2);
1556 for (i = 0; i < alength(v7); i++)
1557 pout.putf("%s\n", pconst(string(aget(v7, i))));
1558 }
1559
1560
1561 //
1562 // main
1563 //
1564
1565
main()1566 int main()
1567 {
1568 try
1569 {
1570 string_test1();
1571 string_test2();
1572 string_benchmarks();
1573 cset_test();
1574
1575 podlist_test();
1576 ptrlist_test();
1577 strlist_test();
1578 textmap_test();
1579
1580 outfile_test();
1581 infile_test();
1582 mem_test();
1583 md5_test();
1584 putf_test();
1585
1586 time_test();
1587 variant_test();
1588
1589 inet_test1();
1590
1591 #ifndef PTYPES_ST
1592 pipe_test();
1593
1594 jobqueue_test();
1595 thread_test();
1596 rwlock_test();
1597 trigger_test();
1598
1599 inet_test2();
1600 inet_test3();
1601 #endif
1602
1603 }
1604
1605 catch (estream* e)
1606 {
1607 perr.putf("\nError: %s\n", pconst(e->get_message()));
1608 exit(1);
1609 delete e;
1610 }
1611
1612 #ifdef DEBUG
1613 if (stralloc != 0 || objalloc != 0)
1614 {
1615 perr.putf("DBG stralloc: %d, objalloc: %d\n", stralloc, objalloc);
1616 fatal(255, "Allocation problems");
1617 }
1618 #endif
1619
1620 return 0;
1621 }
1622
1623