vrpn 07.36
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
vrpn_Shared.C
Go to the documentation of this file.
1#include <math.h> // for floor, fmod
2#include <stddef.h> // for size_t
3#include <stdio.h> // for fprintf() and such
4#include <ctime>
5
6#ifdef _MSC_VER
7// Don't tell us about strcpy being dangerous.
8#define _CRT_SECURE_NO_WARNINGS 1
9#pragma warning(disable : 4996)
10#endif
11
12#include "vrpn_Shared.h"
13
14#if defined(__APPLE__) || defined(__ANDROID__)
15#include <unistd.h>
16#endif
17
18#if !(defined(_WIN32) && defined(VRPN_USE_WINSOCK_SOCKETS))
19#include <sys/select.h> // for select
20#include <netinet/in.h> // for htonl, htons
21#endif
22
23#define CHECK(a) \
24 if (a == -1) return -1
25
26#if defined(VRPN_USE_WINSOCK_SOCKETS)
27/* from HP-UX */\
28/* minGW guards the definition, so we do so here as well.
29minGW declares more, so we are not defining _TIMEZONE_DEFINED. */
30#ifndef _TIMEZONE_DEFINED /* also in sys/time.h */
31struct timezone {
32 int tz_minuteswest; /* minutes west of Greenwich */
33 int tz_dsttime; /* type of dst correction */
34};
35#endif /* _TIMEZONE_DEFINED */
36#endif
37
38// perform normalization of a timeval
39// XXX this still needs to be checked for errors if the timeval
40// or the rate is negative
41static inline void timevalNormalizeInPlace(timeval &in_tv)
42{
43 const long div_77777 = (in_tv.tv_usec / 1000000);
44 in_tv.tv_sec += div_77777;
45 in_tv.tv_usec -= (div_77777 * 1000000);
46}
47timeval vrpn_TimevalNormalize(const timeval &in_tv)
48{
49 timeval out_tv = in_tv;
50 timevalNormalizeInPlace(out_tv);
51 return out_tv;
52}
53
54// Calcs the sum of tv1 and tv2. Returns the sum in a timeval struct.
55// Calcs negative times properly, with the appropriate sign on both tv_sec
56// and tv_usec (these signs will match unless one of them is 0).
57// NOTE: both abs(tv_usec)'s must be < 1000000 (ie, normal timeval format)
58timeval vrpn_TimevalSum(const timeval &tv1, const timeval &tv2)
59{
60 timeval tvSum = tv1;
61
62 tvSum.tv_sec += tv2.tv_sec;
63 tvSum.tv_usec += tv2.tv_usec;
64
65 // do borrows, etc to get the time the way i want it: both signs the same,
66 // and abs(usec) less than 1e6
67 if (tvSum.tv_sec > 0) {
68 if (tvSum.tv_usec < 0) {
69 tvSum.tv_sec--;
70 tvSum.tv_usec += 1000000;
71 }
72 else if (tvSum.tv_usec >= 1000000) {
73 tvSum.tv_sec++;
74 tvSum.tv_usec -= 1000000;
75 }
76 }
77 else if (tvSum.tv_sec < 0) {
78 if (tvSum.tv_usec > 0) {
79 tvSum.tv_sec++;
80 tvSum.tv_usec -= 1000000;
81 }
82 else if (tvSum.tv_usec <= -1000000) {
83 tvSum.tv_sec--;
84 tvSum.tv_usec += 1000000;
85 }
86 }
87 else {
88 // == 0, so just adjust usec
89 if (tvSum.tv_usec >= 1000000) {
90 tvSum.tv_sec++;
91 tvSum.tv_usec -= 1000000;
92 }
93 else if (tvSum.tv_usec <= -1000000) {
94 tvSum.tv_sec--;
95 tvSum.tv_usec += 1000000;
96 }
97 }
98
99 return tvSum;
100}
101
102// Calcs the diff between tv1 and tv2. Returns the diff in a timeval struct.
103// Calcs negative times properly, with the appropriate sign on both tv_sec
104// and tv_usec (these signs will match unless one of them is 0)
105timeval vrpn_TimevalDiff(const timeval &tv1, const timeval &tv2)
106{
107 timeval tv;
108
109 tv.tv_sec = -tv2.tv_sec;
110 tv.tv_usec = -tv2.tv_usec;
111
112 return vrpn_TimevalSum(tv1, tv);
113}
114
115timeval vrpn_TimevalScale(const timeval &tv, double scale)
116{
117 timeval result;
118 result.tv_sec = (long)(tv.tv_sec * scale);
119 result.tv_usec =
120 (long)(tv.tv_usec * scale + fmod(tv.tv_sec * scale, 1.0) * 1000000.0);
121 timevalNormalizeInPlace(result);
122 return result;
123}
124
125// returns 1 if tv1 is greater than tv2; 0 otherwise
126bool vrpn_TimevalGreater(const timeval &tv1, const timeval &tv2)
127{
128 if (tv1.tv_sec > tv2.tv_sec) return 1;
129 if ((tv1.tv_sec == tv2.tv_sec) && (tv1.tv_usec > tv2.tv_usec)) return 1;
130 return 0;
131}
132
133// return 1 if tv1 is equal to tv2; 0 otherwise
134bool vrpn_TimevalEqual(const timeval &tv1, const timeval &tv2)
135{
136 if (tv1.tv_sec == tv2.tv_sec && tv1.tv_usec == tv2.tv_usec)
137 return true;
138 else
139 return false;
140}
141
142unsigned long vrpn_TimevalDuration(struct timeval endT, struct timeval startT)
143{
144 return (endT.tv_usec - startT.tv_usec) +
145 1000000L * (endT.tv_sec - startT.tv_sec);
146}
147
148double vrpn_TimevalDurationSeconds(struct timeval endT, struct timeval startT)
149{
150 return (endT.tv_usec - startT.tv_usec) / 1000000.0 +
151 (endT.tv_sec - startT.tv_sec);
152}
153
154double vrpn_TimevalMsecs(const timeval &tv)
155{
156 return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
157}
158
159timeval vrpn_MsecsTimeval(const double dMsecs)
160{
161 timeval tv;
162 tv.tv_sec = (long)floor(dMsecs / 1000.0);
163 tv.tv_usec = (long)((dMsecs / 1000.0 - tv.tv_sec) * 1e6);
164 return tv;
165}
166
167// Sleep for dMsecs milliseconds, freeing up the processor while you
168// are doing so.
169
170void vrpn_SleepMsecs(double dMilliSecs)
171{
172#if defined(_WIN32)
173 Sleep((DWORD)dMilliSecs);
174#else
175 timeval timeout;
176
177 // Convert milliseconds to seconds
178 timeout.tv_sec = (int)(dMilliSecs / 1000.0);
179
180 // Subtract of whole number of seconds
181 dMilliSecs -= timeout.tv_sec * 1000;
182
183 // Convert remaining milliseconds to microsec
184 timeout.tv_usec = (int)(dMilliSecs * 1000);
185
186 // A select() with NULL file descriptors acts like a microsecond
187 // timer.
188 select(0, 0, 0, 0, &timeout); // wait for that long;
189#endif
190}
191
192// convert vrpn_float64 to/from network order
193// I have chosen big endian as the network order for vrpn_float64
194// to match the standard for htons() and htonl().
195// NOTE: There is an added complexity when we are using an ARM
196// processor in mixed-endian mode for the doubles, whereby we need
197// to not just swap all of the bytes but also swap the two 4-byte
198// words to get things in the right order.
199#if defined(__arm__)
200#include <endian.h>
201#endif
202
203vrpn_float64 vrpn_htond(vrpn_float64 d)
204{
205 if (!vrpn_big_endian) {
206 vrpn_float64 dSwapped;
207 char *pchSwapped = (char *)&dSwapped;
208 char *pchOrig = (char *)&d;
209
210 // swap to big-endian order.
211 unsigned i;
212 for (i = 0; i < sizeof(vrpn_float64); i++) {
213 pchSwapped[i] = pchOrig[sizeof(vrpn_float64) - i - 1];
214 }
215
216#if defined(__arm__) && !defined(__ANDROID__)
217// On ARM processor, see if we're in mixed mode. If so,
218// we need to swap the two words after doing the total
219// swap of bytes.
220#if __FLOAT_WORD_ORDER != __BYTE_ORDER
221 {
222 /* Fixup mixed endian floating point machines */
223 vrpn_uint32 *pwSwapped = (vrpn_uint32 *)&dSwapped;
224 vrpn_uint32 scratch = pwSwapped[0];
225 pwSwapped[0] = pwSwapped[1];
226 pwSwapped[1] = scratch;
227 }
228#endif
229#endif
230
231 return dSwapped;
232 }
233 else {
234 return d;
235 }
236}
237
238// they are their own inverses, so ...
239vrpn_float64 vrpn_ntohd(vrpn_float64 d) { return vrpn_htond(d); }
240
253
254VRPN_API int vrpn_buffer(char **insertPt, vrpn_int32 *buflen, const timeval t)
255{
256 vrpn_int32 sec, usec;
257
258 // tv_sec and usec are 64 bits on some architectures, but we
259 // define them as 32 bit for network transmission
260
261 sec = t.tv_sec;
262 usec = t.tv_usec;
263
264 if (vrpn_buffer(insertPt, buflen, sec)) return -1;
265 return vrpn_buffer(insertPt, buflen, usec);
266}
267
283
284VRPN_API int vrpn_buffer(char **insertPt, vrpn_int32 *buflen,
285 const char *string, vrpn_int32 length)
286{
287 if (length > *buflen) {
288 fprintf(stderr, "vrpn_buffer: buffer not long enough for string.\n");
289 return -1;
290 }
291
292 if (length == -1) {
293 size_t len =
294 strlen(string) + 1; // +1 for the NULL terminating character
295 if (len > (unsigned)*buflen) {
296 fprintf(stderr,
297 "vrpn_buffer: buffer not long enough for string.\n");
298 return -1;
299 }
300 vrpn_strncpynull(*insertPt, string, len);
301 *insertPt += len;
302 *buflen -= static_cast<vrpn_int32>(len);
303 }
304 else {
305 memcpy(*insertPt, string, length);
306 *insertPt += length;
307 *buflen -= length;
308 }
309
310 return 0;
311}
312
324
325VRPN_API int vrpn_unbuffer(const char **buffer, timeval *t)
326{
327 vrpn_int32 sec, usec;
328
329 CHECK(vrpn_unbuffer(buffer, &sec));
330 CHECK(vrpn_unbuffer(buffer, &usec));
331
332 t->tv_sec = sec;
333 t->tv_usec = usec;
334
335 return 0;
336}
337
359
360VRPN_API int vrpn_unbuffer(const char **buffer, char *string, vrpn_int32 length)
361{
362 if (!string) return -1;
363
364 if (length < 0) {
365 // Read the string up to maximum length, then check to make sure we
366 // found the null-terminator in the length we read.
367 size_t max_len = static_cast<size_t>(-length);
368 strncpy(string, *buffer, max_len);
369 size_t i;
370 bool found = false;
371 for (i = 0; i < max_len; i++) {
372 if (string[i] == '\0') {
373 found = true;
374 break;
375 }
376 }
377 if (!found) {
378 return -1;
379 }
380 *buffer += strlen(*buffer) + 1; // +1 for NULL terminating character
381 } else {
382 memcpy(string, *buffer, length);
383 *buffer += length;
384 }
385
386 return 0;
387}
388
389//=====================================================================
390// This section contains various implementations of vrpn_gettimeofday().
391// Which one is selected depends on various #defines. There is a second
392// section that deals with handling various configurations on Windows.
393// The first section deals with the fact that we may want to use the
394// std::chrono classes introduced in C++-11 as a cross-platform (even
395// Windows) solution to timing. If VRPN_USE_STD_CHRONO is defined, then
396// we do this -- converting from chrono epoch and interval into the
397// gettimeofday() standard tick of microseconds and epoch start of
398// midnight, January 1, 1970.
399
401// Implementation with std::chrono follows, and overrides any of
402// the Windows-specific definitions if it is present.
404
405#ifdef VRPN_USE_STD_CHRONO
406#include <chrono>
407
409// With Visual Studio 2013 64-bit, the hires clock produces a clock that has a
410// tick interval of around 15.6 MILLIseconds, repeating the same
411// time between them.
413// With Visual Studio 2015 64-bit, the hires clock produces a good, high-
414// resolution clock with no blips. However, its epoch seems to
415// restart when the machine boots, whereas the system clock epoch
416// starts at the standard midnight January 1, 1970.
418
420// Helper function to convert from the high-resolution clock
421// time to the equivalent system clock time (assuming no clock
422// adjustment on the system clock since program start).
423// To make this thread safe, we semaphore the determination of
424// the offset to be applied. To handle a slow-ticking system
425// clock, we repeatedly sample it until we get a change.
426// This assumes that the high-resolution clock on different
427// threads has the same epoch.
429
430static bool hr_offset_determined = false;
431static vrpn_Semaphore hr_offset_semaphore;
432static struct timeval hr_offset;
433
434static struct timeval high_resolution_time_to_system_time(
435 struct timeval hi_res_time //< Time computed from high-resolution clock
436 )
437{
438 // If we haven't yet determined the offset between the high-resolution
439 // clock and the system clock, do so now. Avoid a race between threads
440 // using the semaphore and checking the boolean both before and after
441 // grabbing the semaphore (in case someone beat us to it).
442 if (!hr_offset_determined) {
443 hr_offset_semaphore.p();
444 // Someone else who had the semaphore may have beaten us to this.
445 if (!hr_offset_determined) {
446 // Watch the system clock until it changes; this will put us
447 // at a tick boundary. On many systems, this will change right
448 // away, but on Windows 8 it will only tick every 16ms or so.
449 std::chrono::system_clock::time_point pre =
450 std::chrono::system_clock::now();
451 std::chrono::system_clock::time_point post;
452 // On Windows 8.1, this took from 1-16 ticks, and seemed to
453 // get offsets to the epoch that were consistent to within
454 // around 1ms.
455 do {
456 post = std::chrono::system_clock::now();
457 } while (pre == post);
458
459 // Now read the high-resolution timer to find out the time
460 // equivalent to the post time on the system clock.
461 std::chrono::high_resolution_clock::time_point high =
462 std::chrono::high_resolution_clock::now();
463
464 // Now convert both the hi-resolution clock time and the
465 // post-tick system clock time into struct timevals and
466 // store the difference between them as the offset.
467 std::time_t high_secs =
468 std::chrono::duration_cast<std::chrono::seconds>(
469 high.time_since_epoch())
470 .count();
471 std::chrono::high_resolution_clock::time_point
472 fractional_high_secs = high - std::chrono::seconds(high_secs);
473 struct timeval high_time;
474 high_time.tv_sec = static_cast<unsigned long>(high_secs);
475 high_time.tv_usec = static_cast<unsigned long>(
476 std::chrono::duration_cast<std::chrono::microseconds>(
477 fractional_high_secs.time_since_epoch())
478 .count());
479
480 std::time_t post_secs =
481 std::chrono::duration_cast<std::chrono::seconds>(
482 post.time_since_epoch())
483 .count();
484 std::chrono::system_clock::time_point fractional_post_secs =
485 post - std::chrono::seconds(post_secs);
486 struct timeval post_time;
487 post_time.tv_sec = static_cast<unsigned long>(post_secs);
488 post_time.tv_usec = static_cast<unsigned long>(
489 std::chrono::duration_cast<std::chrono::microseconds>(
490 fractional_post_secs.time_since_epoch())
491 .count());
492
493 hr_offset = vrpn_TimevalDiff(post_time, high_time);
494
495 // We've found our offset ... re-use it from here on.
496 hr_offset_determined = true;
497 }
498 hr_offset_semaphore.v();
499 }
500
501 // The offset has been determined, by us or someone else. Apply it.
502 return vrpn_TimevalSum(hi_res_time, hr_offset);
503}
504
505int vrpn_gettimeofday(timeval *tp, void *tzp)
506{
507 // If we have nothing to fill in, don't try.
508 if (tp == NULL) {
509 return 0;
510 }
511 struct timezone *timeZone = reinterpret_cast<struct timezone *>(tzp);
512
513 // Find out the time, and how long it has been in seconds since the
514 // epoch.
515 std::chrono::high_resolution_clock::time_point now =
516 std::chrono::high_resolution_clock::now();
517 std::time_t secs =
518 std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch())
519 .count();
520
521 // Subtract the time in seconds from the full time to get a
522 // remainder that is a fraction of a second since the epoch.
523 std::chrono::high_resolution_clock::time_point fractional_secs =
524 now - std::chrono::seconds(secs);
525
526 // Store the seconds and the fractional seconds as microseconds into
527 // the timeval structure. Then convert from the hi-res clock time
528 // to system clock time.
529 struct timeval hi_res_time;
530 hi_res_time.tv_sec = static_cast<unsigned long>(secs);
531 hi_res_time.tv_usec = static_cast<unsigned long>(
532 std::chrono::duration_cast<std::chrono::microseconds>(
533 fractional_secs.time_since_epoch())
534 .count());
535 *tp = high_resolution_time_to_system_time(hi_res_time);
536
537 // @todo Fill in timezone structure with relevant info.
538 if (timeZone != NULL) {
539 timeZone->tz_minuteswest = 0;
540 timeZone->tz_dsttime = 0;
541 }
542
543 return 0;
544}
545
546#else // VRPN_USE_STD_CHRONO
547
549// Implementation without std::chrono follows.
551
553// More accurate gettimeofday() on some Windows operating systems
554// and machines can be gotten by using the Performance Counter
555// on the CPU. This doesn't seem to work in NT/2000 for some
556// computers, so the code to do it is #defined out by default.
557// To put it back back, #define VRPN_UNSAFE_WINDOWS_CLOCK and
558// the following code will use the performance counter (which it
559// takes a second to calibrate at program start-up).
561
562#ifndef VRPN_UNSAFE_WINDOWS_CLOCK
563
564#if defined(_WIN32) && !defined(__CYGWIN__)
565#include <math.h> // for floor, fmod
566
567#pragma optimize("", on)
568
569#ifdef _WIN32
570void get_time_using_GetLocalTime(unsigned long &sec, unsigned long &usec)
571{
572 SYSTEMTIME stime; // System time in funky structure
573 FILETIME ftime; // Time in 100-nsec intervals since Jan 1 1601
574 ULARGE_INTEGER tics; // ftime stored into a 64-bit quantity
575
576 GetLocalTime(&stime);
577 SystemTimeToFileTime(&stime, &ftime);
578
579 // Copy the data into a structure that can be treated as a 64-bit integer
580 tics.HighPart = ftime.dwHighDateTime;
581 tics.LowPart = ftime.dwLowDateTime;
582
583 // Change units (100 nanoseconds --> microseconds)
584 tics.QuadPart /= 10;
585
586 // Subtract the offset between the two clock bases (Jan 1, 1601 --> Jan 1, 1970)
587 tics.QuadPart -= 11644473600000000ULL;
588
589 // Convert the 64-bit time into seconds and microseconds since Jan 1 1970
590 sec = (unsigned long)(tics.QuadPart / 1000000UL);
591 usec = (unsigned long)(tics.QuadPart % 1000000UL);
592}
593#endif
594
596// Although VC++ doesn't include a gettimeofday
597// call, Cygnus Solutions Cygwin32 environment does,
598// so this is not used when compiling with gcc under WIN32.
599// XXX Note that the cygwin one was wrong in an earlier
600// version. It is claimed that they have fixed it now, but
601// better check.
603int vrpn_gettimeofday(timeval *tp, void *tzp)
604{
605 struct timezone *timeZone = reinterpret_cast<struct timezone *>(tzp);
606
607 if (tp != NULL) {
608#ifdef _WIN32_WCE
609 unsigned long sec, usec;
610 get_time_using_GetLocalTime(sec, usec);
611 tp->tv_sec = sec;
612 tp->tv_usec = usec;
613#else
614 struct _timeb t;
615 _ftime(&t);
616 tp->tv_sec = t.time;
617 tp->tv_usec = (long)t.millitm * 1000;
618#endif
619 }
620 if (tzp != NULL) {
621 TIME_ZONE_INFORMATION tz;
622 GetTimeZoneInformation(&tz);
623 timeZone->tz_minuteswest = tz.Bias;
624 timeZone->tz_dsttime = (tz.StandardBias != tz.Bias);
625 }
626 return 0;
627}
628
629#endif // defined(_WIN32)
630
631#else // In the case that VRPN_UNSAFE_WINDOWS_CLOCK is defined
632
633// Although VC++ doesn't include a gettimeofday
634// call, Cygnus Solutions Cygwin32 environment does.
635// XXX AND ITS WRONG in the current release 10/11/99, version b20.1
636// They claim it will be fixed in the next release, version b21
637// so until then, we will make it right using our solution.
638#if defined(_WIN32) && !defined(__CYGWIN__)
639#include <math.h> // for floor, fmod
640
641// utility routines to read the pentium time stamp counter
642// QueryPerfCounter drifts too much -- others have documented this
643// problem on the net
644
645// This is all based on code extracted from the UNC hiball tracker cib lib
646
647// 200 mhz pentium default -- we change this based on our calibration
648static __int64 VRPN_CLOCK_FREQ = 200000000;
649
650// Helium to histidine
651// __int64 FREQUENCY = 199434500;
652
653// tori -- but queryperfcounter returns this for us
654// __int64 FREQUENCY = 198670000;
655
656// Read Time Stamp Counter
657#define rdtsc(li) \
658 { \
659 _asm _emit 0x0f _asm _emit 0x31 _asm mov li.LowPart, \
660 eax _asm mov li.HighPart, edx \
661 }
662
663/*
664 * calculate the time stamp counter register frequency (clock freq)
665 */
666#ifndef VRPN_WINDOWS_CLOCK_V2
667#pragma optimize("", off)
668static int vrpn_AdjustFrequency(void)
669{
670 const int loops = 2;
671 const int tPerLoop = 500; // milliseconds for Sleep()
672 fprintf(stderr, "vrpn vrpn_gettimeofday: determining clock frequency...");
673
674 LARGE_INTEGER startperf, endperf;
675 LARGE_INTEGER perffreq;
676
677 // See if the hardware supports the high-resolution performance counter.
678 // If so, get the frequency of it. If not, we can't use it and so return
679 // -1.
680 if (QueryPerformanceFrequency(&perffreq) == 0) {
681 return -1;
682 }
683
684 // don't optimize away these variables
685 double sum = 0;
686 volatile LARGE_INTEGER liStart, liEnd;
687
688 DWORD dwPriorityClass = GetPriorityClass(GetCurrentProcess());
689 int iThreadPriority = GetThreadPriority(GetCurrentThread());
690 SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
691 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
692
693 // pull all into cache and do rough test to see if tsc and perf counter
694 // are one and the same
695 rdtsc(liStart);
696 QueryPerformanceCounter(&startperf);
697 Sleep(100);
698 rdtsc(liEnd);
699 QueryPerformanceCounter(&endperf);
700
701 double freq = perffreq.QuadPart * (liEnd.QuadPart - liStart.QuadPart) /
702 ((double)(endperf.QuadPart - startperf.QuadPart));
703
704 if (fabs(perffreq.QuadPart - freq) < 0.05 * freq) {
705 VRPN_CLOCK_FREQ = (__int64)perffreq.QuadPart;
706 fprintf(stderr, "\nvrpn vrpn_gettimeofday: perf clock is tsc -- using "
707 "perf clock freq ( %lf MHz)\n",
708 perffreq.QuadPart / 1e6);
709 SetPriorityClass(GetCurrentProcess(), dwPriorityClass);
710 SetThreadPriority(GetCurrentThread(), iThreadPriority);
711 return 0;
712 }
713
714 // either tcs and perf clock are not the same, or we could not
715 // tell accurately enough with the short test. either way we now
716 // need an accurate frequency measure, so ...
717
718 fprintf(stderr, " (this will take %lf seconds)...\n",
719 loops * tPerLoop / 1000.0);
720
721 for (int j = 0; j < loops; j++) {
722 rdtsc(liStart);
723 QueryPerformanceCounter(&startperf);
724 Sleep(tPerLoop);
725 rdtsc(liEnd);
726 QueryPerformanceCounter(&endperf);
727
728 // perf counter timer ran for one call to Query and one call to
729 // tcs read in addition to the time between the tsc readings
730 // tcs read did the same
731
732 // endperf - startperf / perf freq = time between perf queries
733 // endtsc - starttsc = clock ticks between perf queries
734 // sum += (endtsc - starttsc) / ((double)(endperf -
735 // startperf)/perffreq);
736 sum += perffreq.QuadPart * (liEnd.QuadPart - liStart.QuadPart) /
737 ((double)(endperf.QuadPart - startperf.QuadPart));
738 }
739
740 SetPriorityClass(GetCurrentProcess(), dwPriorityClass);
741 SetThreadPriority(GetCurrentThread(), iThreadPriority);
742
743 // might want last, not sum -- just get into cache and run
744 freq = (sum / loops);
745
746 // if we are on a uniprocessor system, then use the freq estimate
747 // This used to check against a 200 mhz assumed clock, but now
748 // we assume the routine works and trust the result.
749 // if (fabs(freq - VRPN_CLOCK_FREQ) > 0.05 * VRPN_CLOCK_FREQ) {
750 // cerr << "vrpn vrpn_gettimeofday: measured freq is " << freq/1e6
751 // << " MHz - DOES NOT MATCH" << endl;
752 // return -1;
753 // }
754
755 // if we are in a system where the perf clock is the tsc, then use the
756 // rate the perf clock returns (or rather, if the freq we measure is
757 // approx the perf clock freq).
758 if (fabs(perffreq.QuadPart - freq) < 0.05 * freq) {
759 VRPN_CLOCK_FREQ = perffreq.QuadPart;
760 fprintf(stderr, "vrpn vrpn_gettimeofday: perf clock is tsc -- using "
761 "perf clock freq ( %lf MHz)\n",
762 perffreq.QuadPart / 1e6);
763 }
764 else {
765 fprintf(stderr, "vrpn vrpn_gettimeofday: adjusted clock freq to "
766 "measured freq ( %lf MHz )\n",
767 freq / 1e6);
768 }
769 VRPN_CLOCK_FREQ = (__int64)freq;
770 return 0;
771}
772#pragma optimize("", on)
773#endif
774
775// The pc has no gettimeofday call, and the closest thing to it is _ftime.
776// _ftime, however, has only about 6 ms resolution, so we use the peformance
777// as an offset from a base time which is established by a call to by _ftime.
778
779// The first call to vrpn_gettimeofday will establish a new time frame
780// on which all later calls will be based. This means that the time returned
781// by vrpn_gettimeofday will not always match _ftime (even at _ftime's
782// resolution),
783// but it will be consistent across all vrpn_gettimeofday calls.
784
786// Although VC++ doesn't include a gettimeofday
787// call, Cygnus Solutions Cygwin32 environment does,
788// so this is not used when compiling with gcc under WIN32
789
790// XXX AND ITS WRONG in the current release 10/11/99
791// They claim it will be fixed in the next release,
792// so until then, we will make it right using our solution.
794#ifndef VRPN_WINDOWS_CLOCK_V2
795int vrpn_gettimeofday(timeval *tp, void *tzp)
796{
797 struct timezone *timeZone = reinterpret_cast<struct timezone *>(tzp);
798 static int fFirst = 1;
799 static int fHasPerfCounter = 1;
800 static struct _timeb tbInit;
801 static LARGE_INTEGER liInit;
802 static LARGE_INTEGER liNow;
803 static LARGE_INTEGER liDiff;
804 timeval tvDiff;
805
806 if (!fHasPerfCounter) {
807 _ftime(&tbInit);
808 tp->tv_sec = tbInit.time;
809 tp->tv_usec = tbInit.millitm * 1000;
810 return 0;
811 }
812
813 if (fFirst) {
814 LARGE_INTEGER liTemp;
815 // establish a time base
816 fFirst = 0;
817
818 // Check to see if we are on a Windows NT machine (as opposed to a
819 // Windows 95/98 machine). If we are not, then use the _ftime code
820 // because the hi-perf clock does not work under Windows 98SE on my
821 // laptop, although the query for one seems to indicate that it is
822 // available.
823
824 {
825 OSVERSIONINFO osvi;
826
827 memset(&osvi, 0, sizeof(OSVERSIONINFO));
828 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
829 GetVersionEx(&osvi);
830
831 if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) {
832 fprintf(stderr,
833 "\nvrpn_gettimeofday: disabling hi performance clock "
834 "on non-NT system. "
835 "Defaulting to _ftime (~6 ms resolution) ...\n");
836 fHasPerfCounter = 0;
837 vrpn_gettimeofday(tp, tzp);
838 return 0;
839 }
840 }
841
842 // check that hi-perf clock is available
843 if (!(fHasPerfCounter = QueryPerformanceFrequency(&liTemp))) {
844 fprintf(stderr,
845 "\nvrpn_gettimeofday: no hi performance clock available. "
846 "Defaulting to _ftime (~6 ms resolution) ...\n");
847 fHasPerfCounter = 0;
848 vrpn_gettimeofday(tp, tzp);
849 return 0;
850 }
851
852 if (vrpn_AdjustFrequency() < 0) {
853 fprintf(stderr,
854 "\nvrpn_gettimeofday: can't verify clock frequency. "
855 "Defaulting to _ftime (~6 ms resolution) ...\n");
856 fHasPerfCounter = 0;
857 vrpn_gettimeofday(tp, tzp);
858 return 0;
859 }
860 // get current time
861 // We assume this machine has a time stamp counter register --
862 // I don't know of an easy way to check for this
863 rdtsc(liInit);
864 _ftime(&tbInit);
865
866 // we now consider it to be exactly the time _ftime returned
867 // (beyond the resolution of _ftime, down to the perfCounter res)
868 }
869
870 // now do the regular get time call to get the current time
871 rdtsc(liNow);
872
873 // find offset from initial value
874 liDiff.QuadPart = liNow.QuadPart - liInit.QuadPart;
875
876 tvDiff.tv_sec = (long)(liDiff.QuadPart / VRPN_CLOCK_FREQ);
877 tvDiff.tv_usec =
878 (long)(1e6 * ((liDiff.QuadPart - VRPN_CLOCK_FREQ * tvDiff.tv_sec) /
879 (double)VRPN_CLOCK_FREQ));
880
881 // pack the value and clean it up
882 tp->tv_sec = tbInit.time + tvDiff.tv_sec;
883 tp->tv_usec = tbInit.millitm * 1000 + tvDiff.tv_usec;
884 while (tp->tv_usec >= 1000000) {
885 tp->tv_sec++;
886 tp->tv_usec -= 1000000;
887 }
888
889 return 0;
890}
891#else // VRPN_WINDOWS_CLOCK_V2 is defined
892
893void get_time_using_GetLocalTime(unsigned long &sec, unsigned long &usec)
894{
895 static LARGE_INTEGER first_count = {0, 0};
896 static unsigned long first_sec, first_usec;
897 static LARGE_INTEGER perf_freq; //< Frequency of the performance counter.
898 SYSTEMTIME stime; // System time in funky structure
899 FILETIME ftime; // Time in 100-nsec intervals since Jan 1 1601
900 LARGE_INTEGER tics; // ftime stored into a 64-bit quantity
901 LARGE_INTEGER perf_counter;
902
903 // The first_count value will be zero only the first time through; we
904 // rely on this to set up the structures needed to interpret the data
905 // that we get from querying the performance counter.
906 if (first_count.QuadPart == 0) {
907 QueryPerformanceCounter(&first_count);
908
909 // Find out how fast the performance counter runs. Store this for later
910 // runs.
911 QueryPerformanceFrequency(&perf_freq);
912
913 // Find out what time it is in a Windows structure.
914 GetLocalTime(&stime);
915 SystemTimeToFileTime(&stime, &ftime);
916
917 // Copy the data into a structure that can be treated as a 64-bit
918 // integer
919 tics.HighPart = ftime.dwHighDateTime;
920 tics.LowPart = ftime.dwLowDateTime;
921
922 // Convert the 64-bit time into seconds and microseconds since July 1
923 // 1601
924 sec = (long)(tics.QuadPart / 10000000L);
925 usec = (long)((tics.QuadPart - (((LONGLONG)(sec)) * 10000000L)) / 10);
926 first_sec = sec;
927 first_usec = usec;
928 }
929 else {
930 QueryPerformanceCounter(&perf_counter);
931 if (perf_counter.QuadPart >= first_count.QuadPart) {
932 perf_counter.QuadPart =
933 perf_counter.QuadPart - first_count.QuadPart;
934 }
935 else {
936 // Take care of the case when the counter rolls over.
937 perf_counter.QuadPart = 0x7fffffffffffffffLL -
938 first_count.QuadPart +
939 perf_counter.QuadPart;
940 }
941
942 // Reinterpret the performance counter into seconds and microseconds
943 // by dividing by the performance counter. Microseconds is placed
944 // into perf_counter by subtracting the seconds value out, then
945 // multiplying by 1 million and re-dividing by the performance counter.
946 sec = (long)(perf_counter.QuadPart / perf_freq.QuadPart);
947 perf_counter.QuadPart -= perf_freq.QuadPart * sec;
948 perf_counter.QuadPart *= 1000000L; //< Turn microseconds into seconds
949 usec = first_usec + (long)(perf_counter.QuadPart / perf_freq.QuadPart);
950 sec += first_sec;
951
952 // Make sure that we've not got more than a million microseconds.
953 // If so, then shift it into seconds. We don't expect it to be above
954 // more than 1 million because we added two things, each of which were
955 // less than 1 million.
956 if (usec > 1000000L) {
957 usec -= 1000000L;
958 sec++;
959 }
960 }
961
962 // Translate the time to be based on January 1, 1970 (_ftime base)
963 // The offset here is gotten by using the "time_test" program to report the
964 // difference in seconds between the two clocks.
965 sec -= 3054524608L;
966}
967
968int vrpn_gettimeofday(timeval *tp, void *tzp)
969{
970 struct timezone *timeZone = reinterpret_cast<struct timezone *>(tzp);
971 unsigned long sec, usec;
972 get_time_using_GetLocalTime(sec, usec);
973 tp->tv_sec = sec;
974 tp->tv_usec = usec;
975 if (tzp != NULL) {
976 TIME_ZONE_INFORMATION tz;
977 GetTimeZoneInformation(&tz);
978 timeZone->tz_minuteswest = tz.Bias;
979 timeZone->tz_dsttime = (tz.StandardBias != tz.Bias);
980 }
981 return 0;
982}
983
984#endif // defined(VRPN_WINDOWS_CLOCK_V2)
985
986#endif // defined(_WIN32)
987
988// do the calibration before the program ever starts up
989static timeval __tv;
990static int __iTrash = vrpn_gettimeofday(&__tv, (struct timezone *)NULL);
991
992#endif // VRPN_UNSAFE_WINDOWS_CLOCK
993
994#endif // VRPN_USE_STD_CHRONO
995
996// End of the section dealing with vrpn_gettimeofday()
997//=====================================================================
998
1000{
1001 // Get a buffer to use that is large enough to test all of the routines.
1002 vrpn_float64 dbuffer[256];
1003 vrpn_int32 buflen;
1004
1005 vrpn_float64 in_float64 = 42.1;
1006 vrpn_int32 in_int32 = 17;
1007 vrpn_uint16 in_uint16 = 397;
1008 vrpn_uint8 in_uint8 = 1;
1009
1010 vrpn_float64 out_float64;
1011 vrpn_int32 out_int32;
1012 vrpn_uint16 out_uint16;
1013 vrpn_uint8 out_uint8;
1014
1015 // Test packing using little-endian routines.
1016 // IMPORTANT: Do these from large to small to get good alignment.
1017 char *bufptr = (char *)dbuffer;
1018 buflen = sizeof(dbuffer);
1019 if (vrpn_buffer_to_little_endian(&bufptr, &buflen, in_float64) != 0) {
1020 fprintf(stderr,
1021 "vrpn_test_pack_unpack(): Could not buffer little endian\n");
1022 return false;
1023 }
1024 if (vrpn_buffer_to_little_endian(&bufptr, &buflen, in_int32) != 0) {
1025 fprintf(stderr,
1026 "vrpn_test_pack_unpack(): Could not buffer little endian\n");
1027 return false;
1028 }
1029 if (vrpn_buffer_to_little_endian(&bufptr, &buflen, in_uint16) != 0) {
1030 fprintf(stderr,
1031 "vrpn_test_pack_unpack(): Could not buffer little endian\n");
1032 return false;
1033 }
1034 if (vrpn_buffer_to_little_endian(&bufptr, &buflen, in_uint8) != 0) {
1035 fprintf(stderr,
1036 "vrpn_test_pack_unpack(): Could not buffer little endian\n");
1037 return false;
1038 }
1039
1040 // Test unpacking using little-endian routines.
1041 bufptr = (char *)dbuffer;
1042 if (in_float64 !=
1043 (out_float64 =
1044 vrpn_unbuffer_from_little_endian<vrpn_float64>(bufptr))) {
1045 fprintf(stderr,
1046 "vrpn_test_pack_unpack(): Could not unbuffer little endian\n");
1047 return false;
1048 }
1049 if (in_int32 !=
1050 (out_int32 = vrpn_unbuffer_from_little_endian<vrpn_int32>(bufptr))) {
1051 fprintf(stderr,
1052 "vrpn_test_pack_unpack(): Could not unbuffer little endian\n");
1053 return false;
1054 }
1055 if (in_uint16 !=
1056 (out_uint16 = vrpn_unbuffer_from_little_endian<vrpn_uint16>(bufptr))) {
1057 fprintf(stderr,
1058 "vrpn_test_pack_unpack(): Could not unbuffer little endian\n");
1059 return false;
1060 }
1061 if (in_uint8 !=
1062 (out_uint8 = vrpn_unbuffer_from_little_endian<vrpn_uint8>(bufptr))) {
1063 fprintf(stderr,
1064 "vrpn_test_pack_unpack(): Could not unbuffer little endian\n");
1065 return false;
1066 }
1067
1068 // Test packing using big-endian routines.
1069 bufptr = (char *)dbuffer;
1070 buflen = sizeof(dbuffer);
1071 if (vrpn_buffer(&bufptr, &buflen, in_float64) != 0) {
1072 fprintf(stderr,
1073 "vrpn_test_pack_unpack(): Could not buffer big endian\n");
1074 return false;
1075 }
1076 if (vrpn_buffer(&bufptr, &buflen, in_int32) != 0) {
1077 fprintf(stderr,
1078 "vrpn_test_pack_unpack(): Could not buffer big endian\n");
1079 return false;
1080 }
1081 if (vrpn_buffer(&bufptr, &buflen, in_uint16) != 0) {
1082 fprintf(stderr,
1083 "vrpn_test_pack_unpack(): Could not buffer big endian\n");
1084 return false;
1085 }
1086 if (vrpn_buffer(&bufptr, &buflen, in_uint8) != 0) {
1087 fprintf(stderr,
1088 "vrpn_test_pack_unpack(): Could not buffer big endian\n");
1089 return false;
1090 }
1091
1092 // Test unpacking using big-endian routines.
1093 bufptr = (char *)dbuffer;
1094 if (in_float64 != (out_float64 = vrpn_unbuffer<vrpn_float64>(bufptr))) {
1095 fprintf(stderr,
1096 "vrpn_test_pack_unpack(): Could not unbuffer big endian\n");
1097 return false;
1098 }
1099 if (in_int32 != (out_int32 = vrpn_unbuffer<vrpn_int32>(bufptr))) {
1100 fprintf(stderr,
1101 "vrpn_test_pack_unpack(): Could not unbuffer big endian\n");
1102 return false;
1103 }
1104 if (in_uint16 != (out_uint16 = vrpn_unbuffer<vrpn_uint16>(bufptr))) {
1105 fprintf(stderr,
1106 "vrpn_test_pack_unpack(): Could not unbuffer big endian\n");
1107 return false;
1108 }
1109 if (in_uint8 != (out_uint8 = vrpn_unbuffer<vrpn_uint8>(bufptr))) {
1110 fprintf(stderr,
1111 "vrpn_test_pack_unpack(): Could not unbuffer big endian\n");
1112 return false;
1113 }
1114
1115 // XXX Test pack/unpack of all other types.
1116
1117 // Test packing little-endian and unpacking big-endian; they should
1118 // be different.
1119 bufptr = (char *)dbuffer;
1120 buflen = sizeof(dbuffer);
1121 if (vrpn_buffer_to_little_endian(&bufptr, &buflen, in_float64) != 0) {
1122 fprintf(stderr,
1123 "vrpn_test_pack_unpack(): Could not buffer little endian\n");
1124 return false;
1125 }
1126 bufptr = (char *)dbuffer;
1127 if (in_float64 == (out_float64 = vrpn_unbuffer<vrpn_float64>(bufptr))) {
1128 fprintf(
1129 stderr,
1130 "vrpn_test_pack_unpack(): Cross-packing produced same result\n");
1131 return false;
1132 }
1133
1134 return true;
1135}
1136
1138{
1139 // Test the default constructor and ensure that the destructor doesn't crash.
1140 {
1142 if ((v0.size() != 0) || (v0.data() != 0)) {
1143 fprintf(stderr,"vrpn_test_vrpn_vector(): Default constructor failed\n");
1144 return false;
1145 }
1146 }
1147
1148 // Test the sized contructor, writing to the vector, the copy
1149 // constructor, and the [] operator.
1150 {
1151 vrpn_uint32 count = 19;
1152 vrpn_vector<vrpn_uint32> v1(count);
1153 if (v1.size() != count) {
1154 fprintf(stderr,"vrpn_test_vrpn_vector(): Sized constructor size failed\n");
1155 return false;
1156 }
1157 for (vrpn_uint32 i = 0; i < v1.size(); i++) {
1158 v1[i] = i;
1159 }
1161 if (v2.size() != count) {
1162 fprintf(stderr,"vrpn_test_vrpn_vector(): Copy constructor size failed\n");
1163 return false;
1164 }
1165 for (vrpn_uint32 i = 0; i < v2.size(); i++) {
1166 if (v1[i] != i) {
1167 fprintf(stderr,"vrpn_test_vrpn_vector(): Copy constructor data failed\n");
1168 return false;
1169 }
1170 }
1171 }
1172
1173 // Test range constructor
1174 {
1175 vrpn_uint32 count = 19;
1176 vrpn_vector<vrpn_uint32> v1(count);
1177 for (vrpn_uint32 i = 0; i < v1.size(); i++) {
1178 v1[i] = i;
1179 }
1180 vrpn_vector<vrpn_uint32> v2(&v1.data()[1], &v1.data()[10]);
1181 if (v2.size() != 9) {
1182 fprintf(stderr,"vrpn_test_vrpn_vector(): Range constructor size failed\n");
1183 return false;
1184 }
1185 for (vrpn_uint32 i = 0; i < v2.size(); i++) {
1186 if (v2[i] != i+1) {
1187 fprintf(stderr,"vrpn_test_vrpn_vector(): Range constructor data failed\n");
1188 return false;
1189 }
1190 }
1191 }
1192
1193 // Test data() push_back() and empty().
1194 {
1196 if (!v1.empty()) {
1197 fprintf(stderr,"vrpn_test_vrpn_vector(): Empty failed when empty\n");
1198 return false;
1199 }
1200 v1.push_back(1);
1201 if (v1.empty()) {
1202 fprintf(stderr,"vrpn_test_vrpn_vector(): Empty failed when not empty\n");
1203 return false;
1204 }
1205 if (v1.data()[0] != 1) {
1206 fprintf(stderr,"vrpn_test_vrpn_vector(): data() failed on push_back\n");
1207 return false;
1208 }
1209 }
1210
1211 // Test resize().
1212 {
1214 v1.push_back(1);
1215 v1.push_back(2);
1216 v1.resize(2);
1217 if (v1.size() != 2) {
1218 fprintf(stderr,"vrpn_test_vrpn_vector(): resize failed on do nothing\n");
1219 return false;
1220 }
1221 if ((v1[0] != 1) || (v1[1] != 2)) {
1222 fprintf(stderr,"vrpn_test_vrpn_vector(): resize data failed on do nothing\n");
1223 return false;
1224 }
1225 v1.resize(1);
1226 if (v1.size() != 1) {
1227 fprintf(stderr,"vrpn_test_vrpn_vector(): resize failed on shrink\n");
1228 return false;
1229 }
1230 if ((v1[0] != 1)) {
1231 fprintf(stderr,"vrpn_test_vrpn_vector(): resize data failed on shrink\n");
1232 return false;
1233 }
1234 v1.resize(10);
1235 if (v1.size() != 10) {
1236 fprintf(stderr,"vrpn_test_vrpn_vector(): resize failed on grow\n");
1237 return false;
1238 }
1239 if ((v1[0] != 1)) {
1240 fprintf(stderr,"vrpn_test_vrpn_vector(): resize data failed on grow\n");
1241 return false;
1242 }
1243 }
1244
1245 // Test front(), back(), assign(), and clear()
1246 {
1248 v1.push_back(1);
1249 v1.push_back(2);
1250 if (v1.front() != 1) {
1251 fprintf(stderr,"vrpn_test_vrpn_vector(): front failed\n");
1252 return false;
1253 }
1254 if (v1.back() != 2) {
1255 fprintf(stderr,"vrpn_test_vrpn_vector(): back failed\n");
1256 return false;
1257 }
1259 vrpn_uint32 count = 2;
1260 v2.assign(count, 7);
1261 if ((v2[0] != 7) || (v2[1] != 7)) {
1262 fprintf(stderr,"vrpn_test_vrpn_vector(): assign value failed\n");
1263 return false;
1264 }
1265 v2.assign(&v1[0], &v1[2]);
1266 if ((v2[0] != 1) || (v2[1] != 2)) {
1267 fprintf(stderr,"vrpn_test_vrpn_vector(): assign iterators failed\n");
1268 return false;
1269 }
1270 v1.clear();
1271 if (v1.size() != 0) {
1272 fprintf(stderr,"vrpn_test_vrpn_vector(): clear failed\n");
1273 return false;
1274 }
1275 }
1276
1277 // Test begin() and end()
1278 {
1279 vrpn_vector<vrpn_uint32> v0, v1(10);
1280 if ((v0.begin() != 0) || (v0.end() != 0)) {
1281 fprintf(stderr,"vrpn_test_vrpn_vector(): empty begin/end failed\n");
1282 return false;
1283 }
1284 if ((v1.begin() != v1.data()) || (v1.end() != &v1.data()[10])) {
1285 fprintf(stderr,"vrpn_test_vrpn_vector(): begin/end failed\n");
1286 return false;
1287 }
1288 }
1289
1290 // Test operator =
1291 {
1293 v0.push_back(1);
1294 v0.push_back(2);
1295 v1 = v0;
1296 v1[0] = 3;
1297 if ((v1[1] != 2) || (v0[0] != 1)) {
1298 fprintf(stderr, "vrpn_test_vrpn_vector(): operator = failed (%d, %d)\n", v0[0], v1[1]);
1299 return false;
1300 }
1301 }
1302
1303 return true;
1304}
int v()
Release of resource. ("up")
int p()
Blocking acquire of resource. ("down")
bool empty() const
iterator end()
void push_back(const T &val)
size_type size() const
void resize(size_type s)
void assign(size_type count, const T &value)
iterator begin()
#define VRPN_API
VRPN_API int vrpn_unbuffer(const char **buffer, timeval *t)
Utility routine for taking a struct timeval from a buffer that was sent as a message.
unsigned long vrpn_TimevalDuration(struct timeval endT, struct timeval startT)
Return number of microseconds between startT and endT.
bool vrpn_test_pack_unpack(void)
bool vrpn_test_vrpn_vector(void)
double vrpn_TimevalDurationSeconds(struct timeval endT, struct timeval startT)
Return the number of seconds between startT and endT as a floating-point value.
#define CHECK(a)
Definition vrpn_Shared.C:23
bool vrpn_TimevalGreater(const timeval &tv1, const timeval &tv2)
timeval vrpn_TimevalScale(const timeval &tv, double scale)
VRPN_API int vrpn_buffer(char **insertPt, vrpn_int32 *buflen, const timeval t)
Utility routine for placing a timeval struct into a buffer that is to be sent as a message.
vrpn_float64 vrpn_ntohd(vrpn_float64 d)
vrpn_float64 vrpn_htond(vrpn_float64 d)
double vrpn_TimevalMsecs(const timeval &tv)
timeval vrpn_TimevalDiff(const timeval &tv1, const timeval &tv2)
timeval vrpn_TimevalNormalize(const timeval &in_tv)
Definition vrpn_Shared.C:47
bool vrpn_TimevalEqual(const timeval &tv1, const timeval &tv2)
void vrpn_SleepMsecs(double dMilliSecs)
timeval vrpn_MsecsTimeval(const double dMsecs)
timeval vrpn_TimevalSum(const timeval &tv1, const timeval &tv2)
Definition vrpn_Shared.C:58
#define vrpn_gettimeofday
int vrpn_buffer_to_little_endian(ByteT **insertPt, vrpn_int32 *buflen, const T inVal)
Function template to buffer values to a buffer stored in little- endian order. Specify the type to bu...
char * vrpn_strncpynull(char *dst, const char *src, size_t size)
Version of strncpy that ensures the resulting string is alyways NULL terminated. It also only writes ...