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