2 #define _POSIX_C_SOURCE 200112L
14 #include <sodium/randombytes.h>
22 #include "ed25519/ed25519.h"
24 // additional leading zero is added by C
25 static const char * const pkprefix
= "== ed25519v1-public: type0 ==\0\0";
26 #define pkprefixlen (29 + 3)
27 static const char * const skprefix
= "== ed25519v1-secret: type0 ==\0\0";
28 #define skprefixlen (29 + 3)
29 static const char * const checksumstr
= ".onion checksum";
30 #define checksumstrlen 15
33 static char *workdir
= 0;
34 static size_t workdirlen
= 0;
36 static int quietflag
= 0;
41 // with checksum + version num
42 #define PUBONION_LEN (PUBLIC_LEN + 3)
43 // with newline included
46 static size_t onionendpos
; // end of .onion within string
47 static size_t direndpos
; // end of dir before .onion within string
48 static size_t printstartpos
; // where to start printing from
49 static size_t printlen
; // precalculated, related to printstartpos
51 static pthread_mutex_t fout_mutex
;
53 static size_t numneedgenerate
= 0;
54 static pthread_mutex_t keysgenerated_mutex
;
55 static volatile size_t keysgenerated
= 0;
56 static volatile int endwork
= 0;
58 static void termhandler(int sig
)
72 size_t len
; // real len minus one
86 VEC_STRUCT(ifiltervec
,struct intfilter
) ifilters
;
91 VEC_STRUCT(bfiltervec
,struct binfilter
) bfilters
;
94 static void filters_init()
104 // o - old filter, n - new
105 // return -1 - old stays, 0 - no conflict, 1 - new overrides old
106 // assumes masked bits are cleared already
108 static inline int ifilter_conflict(struct intfilter
*o
,struct intfilter
*n
)
110 if ((o
->f
& n
->m
) != (n
->f
& o
->m
))
112 // determine which filter contain less bits
118 static inline int ifilter_conflict(struct intfilter
*o
,struct intfilter
*n
,IFT mask
)
120 if ((o
->f
& mask
) != (n
->f
& ifiltermask
))
122 if (ifiltermask
<= mask
)
128 // o - old filter, n - new
129 // return: -1 - old stays, 0 - no conflict, 1 - new overrides old
130 // assumes irrelevant bits are cleared already
131 static inline int bfilter_conflict(struct binfilter
*o
,struct binfilter
*n
)
133 for (size_t i
= 0;i
< sizeof(o
->f
);++i
) {
137 else if (i
== n
->len
)
138 oo
= o
->f
[i
] & n
->mask
;
143 else if (i
== o
->len
)
144 nn
= n
->f
[i
] & o
->mask
;
150 // functional filters subset was the same
151 // determine which filter contain less bits
156 if (o
->mask
<= n
->mask
)
165 * raw representation -- FF.FF.F0.00
166 * big endian -- 0xFFFFF000
167 * little endian -- 0x00F0FFFF
168 * b: 0xFFffF000 ^ 0xFFff0000 -> 0x0000F000
169 * 0x0000F000 + 1 -> 0x0000F001
170 * 0x0000F000 & 0x0000F001 -> 0x0000F000 <- shifted mask
171 * 0x0000F000 ^ 0x0000F000 -> 0x00000000 <- direct mask
172 * 0x0000F000 ^ 0x00000000 -> 0x0000F000 <- shifted mask
173 * l: 0x00f0FFff ^ 0x0000FFff -> 0x00f00000
174 * 0x00f00000 + 1 -> 0x00f00001
175 * 0x00f00000 & 0x00f00001 -> 0x00f00000 <- shifted mask
176 * 0x00f00000 ^ 0x00f00000 -> 0x00000000 <- direct mask
177 * 0x00f00000 ^ 0x00000000 -> 0x00f00000 <- shifted mask
179 * b: 0xFFffFFff ^ 0xF0000000 -> 0x0FffFFff
180 * 0x0FffFFff + 1 -> 0x10000000
181 * 0x0FffFFff & 0x10000000 -> 0x00000000 <- shifted mask
182 * 0x0FffFFff ^ 0x00000000 -> 0x0FffFFff <- direct mask
183 * 0x0FffFFff ^ 0x0FffFFff -> 0x00000000 <- shifted mask
184 * l: 0xFFffFFff ^ 0x000000f0 -> 0xFFffFF0f
185 * 0xFFffFF0f + 1 -> 0xFFffFF10
186 * 0xFFffFF0f & 0xFFffFF10 -> 0xFFffFF00 <- shifted mask
187 * 0xFFffFF0f ^ 0xFFffFF00 -> 0x0000000f <- direct mask
188 * 0xFFffFF0f ^ 0x0000000f -> 0xFFffFF00 <- shifted mask
190 * above method doesn't work in some cases. better way:
191 * l: 0x80ffFFff ^ 0x00f0FFff -> 0x800f0000
192 * 0x800f0000 >> 16 -> 0x0000800f
193 * 0x0000800f + 1 -> 0x00008010
194 * 0x0000800f & 0x00008010 -> 0x00008000 <- smask
195 * 0x0000800f ^ 0x00008000 -> 0x0000000f <- dmask
196 * essentially, we have to make direct mask + shifted mask bits worth of information
197 * and then split it into 2 parts
198 * we do not need absolute shifted mask shifting value, just relative to direct mask
199 * 0x0sss00dd - shifted & direct mask combo
200 * 0x000sssdd - combined mask
202 * generate values from 0x00000000 to 0x000sssdd
203 * for each value, realmask <- (val & 0x000000dd) | ((val & 0x000sss00) << relshiftval)
205 * realmask <- (val & 0x000000dd) | ((val << relshiftval) & 0x0sss0000)
207 static inline void ifilter_addflatten(struct intfilter
*ifltr
,IFT mask
)
209 printf(">enter flatten,f:%08X,m:%08X\n",ifltr
->f
,mask
);
210 if (VEC_LENGTH(ifilters
) == 0) {
211 printf(">flatten simple\n");
213 VEC_ADD(ifilters
,*ifltr
);
217 if (ifiltermask
== mask
) {
218 printf(">flatten lucky\n");
219 // lucky, only need to insert at the right place
220 VEC_FOR(ifilters
,i
) {
221 if (VEC_BUF(ifilters
,i
).f
> ifltr
->f
) {
222 VEC_INSERT(ifilters
,i
,*ifltr
);
226 VEC_ADD(ifilters
,*ifltr
);
229 printf(">flatten complicated; em:0x%08X,m:0x%08X\n",ifiltermask
,mask
);
230 IFT cross
= ifiltermask
^ mask
;
231 printf(">cross:%08X\n",cross
);
233 while ((cross
& 1) == 0) {
237 printf(">ishift:%d,cross:%08X\n",ishift
,cross
);
238 IFT smask
= cross
& (cross
+ 1); // shift mask
239 printf(">smask:%08X\n",smask
);
240 IFT dmask
= cross
^ smask
; // direct mask
241 printf(">dmask:%08X\n",dmask
);
242 IFT cmask
; // combined mask
243 int rshift
= 0; // relative shift
244 while (cmask
= (smask
>> rshift
) | dmask
,(cmask
& (cmask
+ 1)) != 0)
246 printf(">cmask:%08X,rshift:%d\n",cmask
,rshift
);
248 if (ifiltermask
> mask
) {
249 // already existing stuff has more precise mask than we
250 // so we need to flatten our stuff
251 // first find where we should insert
252 VEC_FOR(ifilters
,i
) {
253 if (VEC_BUF(ifilters
,i
).f
> ifltr
->f
) {
255 printf(">before insert:%d\n",(int)VEC_LENGTH(ifilters
));
256 VEC_INSERTN(ifilters
,i
,cmask
+ 1);
257 printf(">after insert:%d\n",(int)VEC_LENGTH(ifilters
));
258 printf(">afterval f:%08X\n",VEC_BUF(ifilters
,i
+cmask
+1).f
);
259 for (size_t j
= 0;;++j
) {
260 VEC_BUF(ifilters
,i
+ j
).f
= ifltr
->f
| (((j
& dmask
) | ((j
<< rshift
) & smask
)) << ishift
);
261 printf(">insert pos:%d,f:%08X\n",
262 (int)(i
+ j
),VEC_BUF(ifilters
,i
+ j
).f
);
269 size_t i
= VEC_LENGTH(ifilters
);
270 VEC_ADDN(ifilters
,cmask
+ 1);
271 for (size_t j
= 0;;++j
) {
272 VEC_BUF(ifilters
,i
+ j
).f
= ifltr
->f
| (j
& dmask
) | ((j
<< rshift
) & smask
);
283 static void filters_add(const char *filter
)
294 memset(&bf
,0,sizeof(bf
));
296 if (!base32_valid(filter
,&ret
)) {
297 fprintf(stderr
, "filter \"%s\" is invalid\n", filter
);
300 ret
= BASE32_FROM_LEN(ret
);
304 if (ret
> sizeof(IFT
))
306 if (ret
> PUBLIC_LEN
)
309 fprintf(stderr
, "filter \"%s\" is too long\n", filter
);
312 ret2
= base32_from(bf
.f
,&bf
.mask
,filter
);
317 for (size_t i
= 0;i
< bf
.len
;++i
)
319 mc
.b
[bf
.len
] = bf
.mask
;
320 memcpy(fc
.b
,bf
.f
,sizeof(fc
.b
));
322 struct intfilter ifltr
= {
328 VEC_FOR(ifilters
,i
) {
331 c
= ifilter_conflict(&VEC_BUF(ifilters
,i
),&ifltr
);
333 c
= ifilter_conflict(&VEC_BUF(ifilters
,i
),&ifltr
,mc
.i
);
336 return; // old filter eats us
338 VEC_REMOVE(ifilters
,i
);
344 ifilter_addflatten(&ifltr
,mc
.i
);
346 VEC_FOR(ifilters
,i
) {
347 // filter with least bits first
348 if (VEC_BUF(ifilters
,i
).m
> ifltr
.m
) {
349 VEC_INSERT(ifilters
,i
,ifltr
);
353 VEC_ADD(ifilters
,ifltr
);
356 VEC_FOR(bfilters
,i
) {
357 int c
= bfilter_conflict(&VEC_BUF(bfilters
,i
),&bf
);
359 return; // old filter eats us
361 VEC_REMOVE(bfilters
,i
);
369 VEC_FOR(bfilters
,i
) {
370 // filter with least bits first
371 if (VEC_BUF(bfilters
,i
).len
> bf
.len
||
372 (VEC_BUF(bfilters
,i
).len
== bf
.len
&&
373 (VEC_BUF(bfilters
,i
).mask
> bf
.mask
)))
375 VEC_INSERT(bfilters
,i
,bf
);
379 VEC_ADD(bfilters
,bf
);
384 static void filters_clean()
393 static size_t filters_count()
396 return VEC_LENGTH(ifilters
);
398 return VEC_LENGTH(bfilters
);
404 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(ifilters);++it)
406 #define MATCHFILTER(it,pk) ((*(IFT *)(pk) & VEC_BUF(ifilters,it).m) == VEC_BUF(ifilters,it).f)
408 #define MATCHFILTER(it,pk) ((*(IFT *)(pk) & ifiltermask) == VEC_BUF(ifilters,it).f)
413 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(bfilters);++it)
414 #define MATCHFILTER(it,pk) ( \
415 memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len) == 0 && \
416 (pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) == VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len])
420 static void loadfilterfile(const char *fname
)
423 FILE *f
= fopen(fname
,"r");
424 while (fgets(buf
,sizeof(buf
),f
)) {
425 for (char *p
= buf
;*p
;++p
) {
431 if (*buf
&& *buf
!= '#' && memcmp(buf
,"//",2) != 0)
436 static void printfilters()
440 l
= VEC_LENGTH(ifilters
);
442 l
= VEC_LENGTH(bfilters
);
445 fprintf(stderr
, "filters:\n");
447 fprintf(stderr
, "no filters defined\n");
449 for (i
= 0;i
< l
;++i
) {
450 char buf0
[256],buf1
[256];
456 imraw
= (u8
*)&VEC_BUF(ifilters
,i
).m
;
458 imraw
= (u8
*)&ifiltermask
;
460 while (len
< sizeof(IFT
) && imraw
[len
] != 0x00) ++len
;
461 u8 mask
= imraw
[len
-1];
462 u8
*ifraw
= (u8
*)&VEC_BUF(ifilters
,i
).f
;
464 size_t len
= VEC_BUF(bfilters
,i
).len
+ 1;
465 u8 mask
= VEC_BUF(bfilters
,i
).mask
;
466 u8
*ifraw
= VEC_BUF(bfilters
,i
).f
;
468 base32_to(buf0
,ifraw
,len
);
469 memcpy(bufx
,ifraw
,len
);
470 bufx
[len
- 1] |= ~mask
;
471 base32_to(buf1
,bufx
,len
);
472 char *a
= buf0
,*b
= buf1
;
473 while (*a
&& *a
== *b
)
476 fprintf(stderr
, "\t%s\n",buf0
);
480 // statistics, if enabled
492 VEC_STRUCT(statsvec
,struct statstruct
);
500 VEC_STRUCT(tstatsvec
,struct tstatstruct
);
504 static void onionready(char *sname
, const u8
*secret
, const u8
*pubonion
)
511 if (numneedgenerate
) {
512 pthread_mutex_lock(&keysgenerated_mutex
);
513 if (keysgenerated
>= numneedgenerate
) {
514 pthread_mutex_unlock(&keysgenerated_mutex
);
519 if (mkdir(sname
,0700) != 0) {
521 pthread_mutex_unlock(&keysgenerated_mutex
);
525 if (numneedgenerate
) {
527 if (keysgenerated
>= numneedgenerate
)
529 pthread_mutex_unlock(&keysgenerated_mutex
);
532 strcpy(&sname
[onionendpos
], "/hs_ed25519_secret_key");
533 fh
= fopen(sname
, "wb");
535 fwrite(secret
, skprefixlen
+ SECRET_LEN
, 1, fh
);
539 strcpy(&sname
[onionendpos
], "/hostname");
540 fh
= fopen(sname
, "w");
542 sname
[onionendpos
] = '\n';
543 fwrite(&sname
[direndpos
], ONIONLEN
+1, 1, fh
);
547 strcpy(&sname
[onionendpos
], "/hs_ed25519_public_key");
548 fh
= fopen(sname
, "wb");
550 fwrite(pubonion
, pkprefixlen
+ PUBLIC_LEN
, 1, fh
);
555 sname
[onionendpos
] = '\n';
556 pthread_mutex_lock(&fout_mutex
);
557 fwrite(&sname
[printstartpos
], printlen
, 1, fout
);
559 pthread_mutex_unlock(&fout_mutex
);
564 static void addseed(u8
*seed
)
566 register unsigned int c
= 1;
567 for (size_t i
= 0;i
< SEED_LEN
;++i
) {
568 c
= (unsigned int)seed
[i
] + c
; seed
[i
] = c
& 0xFF; c
>>= 8;
574 static void *dowork(void *task
)
576 union pubonionunion
{
577 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
584 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
585 u8 secret
[skprefixlen
+ SECRET_LEN
];
586 u8
* const sk
= &secret
[skprefixlen
];
588 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
592 struct statstruct
*st
= (struct statstruct
*)task
;
595 memcpy(secret
,skprefix
,skprefixlen
);
596 memcpy(pubonion
.raw
,pkprefix
,pkprefixlen
);
597 // write version later as it will be overwritten by hash
598 memcpy(hashsrc
,checksumstr
,checksumstrlen
);
599 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
601 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
603 memcpy(sname
,workdir
,workdirlen
);
606 randombytes(seed
,sizeof(seed
));
609 if (unlikely(endwork
))
612 ed25519_seckey_expand(sk
,seed
);
613 ed25519_pubkey(pk
,sk
);
620 if (unlikely(MATCHFILTER(i
,pk
))) {
625 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
626 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
628 pk
[PUBLIC_LEN
+ 2] = 0x03;
630 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
), ".onion");
631 onionready(sname
, secret
, pubonion
.raw
);
643 static void addu64toscalar32(u8
*dst
,u64 v
)
647 for (i
= 0;i
< 32;++i
) {
648 c
+= *dst
+ (v
& 0xFF); *dst
= c
& 0xFF; c
>>= 8;
654 static void *dofastwork(void *task
)
656 union pubonionunion
{
657 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
664 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
665 u8 secret
[skprefixlen
+ SECRET_LEN
];
666 u8
* const sk
= &secret
[skprefixlen
];
668 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
674 struct statstruct
*st
= (struct statstruct
*)task
;
677 memcpy(secret
, skprefix
, skprefixlen
);
678 memcpy(pubonion
.raw
, pkprefix
, pkprefixlen
);
679 // write version later as it will be overwritten by hash
680 memcpy(hashsrc
, checksumstr
, checksumstrlen
);
681 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
683 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
685 memcpy(sname
, workdir
, workdirlen
);
688 randombytes(seed
,sizeof(seed
));
689 ed25519_seckey_expand(sk
,seed
);
691 ge_scalarmult_base(&ge_public
,sk
);
692 ge_p3_tobytes(pk
,&ge_public
);
694 for (counter
= 0;counter
< U64_MAX
-8;counter
+= 8) {
697 if (unlikely(endwork
))
701 if (unlikely(MATCHFILTER(i
,pk
))) {
703 // update secret key with counter
704 addu64toscalar32(sk
,counter
);
706 if (((sk
[0] & 248) == sk
[0]) && (((sk
[31] & 63) | 64) == sk
[31])) {
707 /* These operations should be a no-op. */
717 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
718 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
720 pk
[PUBLIC_LEN
+ 2] = 0x03;
722 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
),".onion");
723 onionready(sname
,secret
,pubonion
.raw
);
724 // don't reuse same seed
730 ge_add(&sum
, &ge_public
,&ge_eightpoint
);
731 ge_p1p1_to_p3(&ge_public
,&sum
);
732 ge_p3_tobytes(pk
,&ge_public
);
744 void printhelp(const char *progname
)
747 "Usage: %s filter [filter...] [options]\n"
748 " %s -f filterfile [options]\n"
750 "\t-h - print help\n"
751 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
752 "\t-q - do not print diagnostic output to stderr\n"
753 "\t-x - do not print onion names\n"
754 "\t-o filename - output onion names to specified file\n"
755 "\t-F - include directory names in onion names output\n"
756 "\t-d dirname - output directory\n"
757 "\t-t numthreads - specify number of threads (default - auto)\n"
758 "\t-j numthreads - same as -t\n"
759 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
760 "\t-z - use faster key generation method. this is now default\n"
761 "\t-Z - use slower key generation method\n"
762 "\t-s - print statistics each 10 seconds\n"
763 "\t-S t - print statistics every specified ammount of seconds\n"
764 "\t-T - do not reset statistics counters when printing\n"
769 void setworkdir(const char *wd
)
772 size_t l
= strlen(wd
);
777 fprintf(stderr
, "unset workdir\n");
783 char *s
= malloc(l
+ needslash
+ 1);
792 fprintf(stderr
,"set workdir: %s\n",workdir
);
795 VEC_STRUCT(threadvec
, pthread_t
);
797 int main(int argc
,char **argv
)
805 struct threadvec threads
;
807 struct statsvec stats
;
808 struct tstatsvec tstats
;
810 int realtimestats
= 1;
818 pthread_mutex_init(&keysgenerated_mutex
, 0);
819 pthread_mutex_init(&fout_mutex
, 0);
821 const char *progname
= argv
[0];
828 if (!ignoreargs
&& *arg
== '-') {
835 fprintf(stderr
, "unrecognised argument: -\n");
841 else if (!strcmp(arg
, "help"))
844 fprintf(stderr
, "unrecognised argument: --%s\n", arg
);
849 else if (*arg
== 0) {
854 else if (*arg
== 'h')
856 else if (*arg
== 'f') {
858 loadfilterfile(*argv
++);
860 fprintf(stderr
, "additional argument required\n");
864 else if (*arg
== 'q')
866 else if (*arg
== 'x')
868 else if (*arg
== 'o') {
872 fprintf(stderr
, "additional argument required\n");
876 else if (*arg
== 'F')
878 else if (*arg
== 'd') {
883 fprintf(stderr
, "additional argument required\n");
886 else if (*arg
== 't' || *arg
== 'j') {
888 numthreads
= atoi(*argv
++);
890 fprintf(stderr
, "additional argument required\n");
894 else if (*arg
== 'n') {
896 numneedgenerate
= (size_t)atoll(*argv
++);
898 fprintf(stderr
, "additional argument required\n");
902 else if (*arg
== 'Z')
904 else if (*arg
== 'z')
906 else if (*arg
== 's') {
908 reportdelay
= 10000000;
910 fprintf(stderr
,"statistics support not compiled in\n");
914 else if (*arg
== 'S') {
917 reportdelay
= (u64
)atoll(*argv
++) * 1000000;
919 fprintf(stderr
, "additional argument required\n");
923 fprintf(stderr
,"statistics support not compiled in\n");
927 else if (*arg
== 'T') {
931 fprintf(stderr
,"statistics support not compiled in\n");
936 fprintf(stderr
, "unrecognised argument: -%c\n", *arg
);
942 else filters_add(arg
);
948 if (!filters_count())
952 fout
= fopen(outfile
, "w");
955 mkdir(workdir
, 0700);
957 direndpos
= workdirlen
;
958 onionendpos
= workdirlen
+ ONIONLEN
;
961 printstartpos
= direndpos
;
962 printlen
= ONIONLEN
+ 1;
965 printlen
= onionendpos
+ 1;
968 if (numthreads
<= 0) {
969 numthreads
= cpucount();
974 signal(SIGTERM
,termhandler
);
975 signal(SIGINT
,termhandler
);
978 VEC_ADDN(threads
,numthreads
);
981 VEC_ADDN(stats
,numthreads
);
984 VEC_ADDN(tstats
,numthreads
);
988 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
) {
991 tp
= &VEC_BUF(stats
,i
);
993 tret
= pthread_create(&VEC_BUF(threads
,i
),0,fastkeygen
? dofastwork
: dowork
,tp
);
995 fprintf(stderr
,"error while making %dth thread: %d\n",(int)i
,tret
);
1001 struct timespec nowtime
;
1002 u64 istarttime
,inowtime
,ireporttime
= 0,elapsedoffset
= 0;
1003 if (clock_gettime(CLOCK_MONOTONIC
,&nowtime
) < 0) {
1004 fprintf(stderr
, "failed to get time\n");
1007 istarttime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
1010 memset(&ts
,0,sizeof(ts
));
1011 ts
.tv_nsec
= 100000000;
1013 if (numneedgenerate
&& keysgenerated
>= numneedgenerate
) {
1020 clock_gettime(CLOCK_MONOTONIC
,&nowtime
);
1021 inowtime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
1022 u64 sumcalc
= 0,sumsuccess
= 0;
1023 for (size_t i
= 0;i
< numthreads
;++i
) {
1026 newt
= VEC_BUF(stats
,i
).numcalc
.v
;
1027 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumcalc
;
1028 VEC_BUF(tstats
,i
).oldnumcalc
= newt
;
1029 VEC_BUF(tstats
,i
).numcalc
+= (u64
)tdiff
;
1030 sumcalc
+= VEC_BUF(tstats
,i
).numcalc
;
1032 newt
= VEC_BUF(stats
,i
).numsuccess
.v
;
1033 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumsuccess
;
1034 VEC_BUF(tstats
,i
).oldnumsuccess
= newt
;
1035 VEC_BUF(tstats
,i
).numsuccess
+= (u64
)tdiff
;
1036 sumsuccess
+= VEC_BUF(tstats
,i
).numsuccess
;
1038 if (reportdelay
&& (!ireporttime
|| inowtime
- ireporttime
>= reportdelay
)) {
1040 ireporttime
+= reportdelay
;
1042 ireporttime
= inowtime
;
1046 double calcpersec
= (1000000.0 * sumcalc
) / (inowtime
- istarttime
);
1047 double successpersec
= (1000000.0 * sumsuccess
) / (inowtime
- istarttime
);
1048 fprintf(stderr
,">calc/sec:%8lf, success/sec:%8lf, elapsed:%5.6lfsec\n",calcpersec
,successpersec
,(inowtime
- istarttime
+ elapsedoffset
) / 1000000.0);
1050 if (realtimestats
) {
1051 for (size_t i
= 0;i
< numthreads
;++i
) {
1052 VEC_BUF(tstats
,i
).numcalc
= 0;
1053 VEC_BUF(tstats
,i
).numsuccess
= 0;
1055 elapsedoffset
+= inowtime
- istarttime
;
1056 istarttime
= inowtime
;
1059 if (sumcalc
> U64_MAX
/ 2) {
1060 for (size_t i
= 0;i
< numthreads
;++i
) {
1061 VEC_BUF(tstats
,i
).numcalc
/= 2;
1062 VEC_BUF(tstats
,i
).numsuccess
/= 2;
1064 u64 timediff
= (inowtime
- istarttime
+ 1) / 2;
1065 elapsedoffset
+= timediff
;
1066 istarttime
+= timediff
;
1072 fprintf(stderr
, "waiting for threads to finish...\n");
1073 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
)
1074 pthread_join(VEC_BUF(threads
,i
),0);
1076 fprintf(stderr
, "done, quitting\n");
1078 pthread_mutex_destroy(&keysgenerated_mutex
);
1079 pthread_mutex_destroy(&fout_mutex
);