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 * essentially, we have to make direct mask + shifted mask bits worth of information
191 * and then split it into 2 parts
192 * we do not need absolute shifted mask shifting value, just relative to direct mask
193 * 0x0sss00dd - shifted & direct mask combo
194 * 0x000sssdd - combined mask
196 * generate values from 0x00000000 to 0x000sssdd
197 * for each value, realmask <- (val & 0x000000dd) | ((val & 0x000sss00) << relshiftval)
199 * realmask <- (val & 0x000000dd) | ((val << relshiftval) & 0x0sss0000)
201 * above method doesn't work in some cases. better way:
202 * l: 0x80ffFFff ^ 0x00f0FFff -> 0x800f0000
203 * 0x800f0000 >> 16 -> 0x0000800f
204 * 0x0000800f + 1 -> 0x00008010
205 * 0x0000800f & 0x00008010 -> 0x00008000 <- smask
206 * 0x0000800f ^ 0x00008000 -> 0x0000000f <- dmask
209 // add expanded set of values
210 // space for that must already be allocated
211 static void ifilter_addexpanded(
212 size_t n
,struct intfilter
*ifltr
,
213 IFT dmask
,IFT smask
,IFT cmask
,
214 int ishift
,int rshift
)
216 for (size_t j
= 0;;++j
) {
217 VEC_BUF(ifilters
,n
+ j
).f
=
218 ifltr
->f
| (((j
& dmask
) | ((j
<< rshift
) & smask
)) << ishift
);
224 // expand existing stuff
225 // allocates needed stuff on its own
226 static void ifilter_expand(IFT dmask
,IFT smask
,IFT cmask
,int ishift
,int rshift
)
228 size_t len
= VEC_LENGTH(ifilters
);
229 printf(">expand:cm:%08X,len:%d,cm*len:%d\n",
230 cmask
,(int)len
,(int)(cmask
* len
));
231 VEC_ADDN(ifilters
,cmask
* len
);
232 printf(">expand after\n");
233 size_t esz
= cmask
+ 1; // size of expanded elements
234 for (size_t i
= len
- 1;;--i
) {
235 for (IFT j
= 0;;++j
) {
236 VEC_BUF(ifilters
,i
* esz
+ j
).f
=
237 VEC_BUF(ifilters
,i
).f
|
238 (((j
& dmask
) | ((j
<< rshift
) & smask
)) << ishift
);
247 static inline void ifilter_addflatten(struct intfilter
*ifltr
,IFT mask
)
249 printf(">enter flatten,f:%08X,m:%08X\n",ifltr
->f
,mask
);
250 if (VEC_LENGTH(ifilters
) == 0) {
251 printf(">flatten simple\n");
253 VEC_ADD(ifilters
,*ifltr
);
257 if (ifiltermask
== mask
) {
258 printf(">flatten lucky\n");
259 // lucky, only need to insert at the right place
260 VEC_FOR(ifilters
,i
) {
261 if (VEC_BUF(ifilters
,i
).f
> ifltr
->f
) {
262 VEC_INSERT(ifilters
,i
,*ifltr
);
266 VEC_ADD(ifilters
,*ifltr
);
269 printf(">flatten complicated; em:0x%08X,m:0x%08X\n",ifiltermask
,mask
);
270 IFT cross
= ifiltermask
^ mask
;
271 printf(">cross:%08X\n",cross
);
273 while ((cross
& 1) == 0) {
277 printf(">ishift:%d,cross:%08X\n",ishift
,cross
);
278 IFT smask
= cross
& (cross
+ 1); // shift mask
279 printf(">smask:%08X\n",smask
);
280 IFT dmask
= cross
^ smask
; // direct mask
281 printf(">dmask:%08X\n",dmask
);
282 IFT cmask
; // combined mask
283 int rshift
= 0; // relative shift
284 while (cmask
= (smask
>> rshift
) | dmask
,(cmask
& (cmask
+ 1)) != 0)
286 printf(">cmask:%08X,rshift:%d\n",cmask
,rshift
);
288 if (ifiltermask
> mask
) {
289 // already existing stuff has more precise mask than we
290 // so we need to expand our stuff
291 // first find where we should insert
292 VEC_FOR(ifilters
,i
) {
293 if (VEC_BUF(ifilters
,i
).f
> ifltr
->f
) {
295 VEC_INSERTN(ifilters
,i
,cmask
+ 1);
296 ifilter_addexpanded(i
,ifltr
,dmask
,smask
,cmask
,ishift
,rshift
);
300 size_t i
= VEC_LENGTH(ifilters
);
301 VEC_ADDN(ifilters
,cmask
+ 1);
302 ifilter_addexpanded(i
,ifltr
,dmask
,smask
,cmask
,ishift
,rshift
);
305 // adjust existing mask
307 // already existing stuff needs to be expanded
308 ifilter_expand(dmask
,smask
,cmask
,ishift
,rshift
);
309 // now just insert our stuff in the right place
310 VEC_FOR(ifilters
,i
) {
311 if (VEC_BUF(ifilters
,i
).f
> ifltr
->f
) {
312 VEC_INSERT(ifilters
,i
,*ifltr
);
316 VEC_ADD(ifilters
,*ifltr
);
322 static void filters_add(const char *filter
)
333 memset(&bf
,0,sizeof(bf
));
335 if (!base32_valid(filter
,&ret
)) {
336 fprintf(stderr
, "filter \"%s\" is invalid\n", filter
);
339 ret
= BASE32_FROM_LEN(ret
);
343 if (ret
> sizeof(IFT
))
345 if (ret
> PUBLIC_LEN
)
348 fprintf(stderr
, "filter \"%s\" is too long\n", filter
);
351 ret2
= base32_from(bf
.f
,&bf
.mask
,filter
);
356 for (size_t i
= 0;i
< bf
.len
;++i
)
358 mc
.b
[bf
.len
] = bf
.mask
;
359 memcpy(fc
.b
,bf
.f
,sizeof(fc
.b
));
361 struct intfilter ifltr
= {
367 VEC_FOR(ifilters
,i
) {
370 c
= ifilter_conflict(&VEC_BUF(ifilters
,i
),&ifltr
);
372 c
= ifilter_conflict(&VEC_BUF(ifilters
,i
),&ifltr
,mc
.i
);
375 return; // old filter eats us
377 VEC_REMOVE(ifilters
,i
);
383 ifilter_addflatten(&ifltr
,mc
.i
);
385 VEC_FOR(ifilters
,i
) {
386 // filter with least bits first
387 if (VEC_BUF(ifilters
,i
).m
> ifltr
.m
) {
388 VEC_INSERT(ifilters
,i
,ifltr
);
392 VEC_ADD(ifilters
,ifltr
);
395 VEC_FOR(bfilters
,i
) {
396 int c
= bfilter_conflict(&VEC_BUF(bfilters
,i
),&bf
);
398 return; // old filter eats us
400 VEC_REMOVE(bfilters
,i
);
408 VEC_FOR(bfilters
,i
) {
409 // filter with least bits first
410 if (VEC_BUF(bfilters
,i
).len
> bf
.len
||
411 (VEC_BUF(bfilters
,i
).len
== bf
.len
&&
412 (VEC_BUF(bfilters
,i
).mask
> bf
.mask
)))
414 VEC_INSERT(bfilters
,i
,bf
);
418 VEC_ADD(bfilters
,bf
);
423 static void filters_clean()
432 static size_t filters_count()
435 return VEC_LENGTH(ifilters
);
437 return VEC_LENGTH(bfilters
);
443 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(ifilters);++it)
445 #define MATCHFILTER(it,pk) ((*(IFT *)(pk) & VEC_BUF(ifilters,it).m) == VEC_BUF(ifilters,it).f)
447 #define MATCHFILTER(it,pk) ((*(IFT *)(pk) & ifiltermask) == VEC_BUF(ifilters,it).f)
452 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(bfilters);++it)
453 #define MATCHFILTER(it,pk) ( \
454 memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len) == 0 && \
455 (pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) == VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len])
459 static void loadfilterfile(const char *fname
)
462 FILE *f
= fopen(fname
,"r");
463 while (fgets(buf
,sizeof(buf
),f
)) {
464 for (char *p
= buf
;*p
;++p
) {
470 if (*buf
&& *buf
!= '#' && memcmp(buf
,"//",2) != 0)
475 static void printfilters()
479 l
= VEC_LENGTH(ifilters
);
481 l
= VEC_LENGTH(bfilters
);
484 fprintf(stderr
, "filters:\n");
486 fprintf(stderr
, "no filters defined\n");
488 for (i
= 0;i
< l
;++i
) {
489 char buf0
[256],buf1
[256];
495 imraw
= (u8
*)&VEC_BUF(ifilters
,i
).m
;
497 imraw
= (u8
*)&ifiltermask
;
499 while (len
< sizeof(IFT
) && imraw
[len
] != 0x00) ++len
;
500 u8 mask
= imraw
[len
-1];
501 u8
*ifraw
= (u8
*)&VEC_BUF(ifilters
,i
).f
;
503 size_t len
= VEC_BUF(bfilters
,i
).len
+ 1;
504 u8 mask
= VEC_BUF(bfilters
,i
).mask
;
505 u8
*ifraw
= VEC_BUF(bfilters
,i
).f
;
507 base32_to(buf0
,ifraw
,len
);
508 memcpy(bufx
,ifraw
,len
);
509 bufx
[len
- 1] |= ~mask
;
510 base32_to(buf1
,bufx
,len
);
511 char *a
= buf0
,*b
= buf1
;
512 while (*a
&& *a
== *b
)
515 fprintf(stderr
, "\t%s\n",buf0
);
519 // statistics, if enabled
531 VEC_STRUCT(statsvec
,struct statstruct
);
539 VEC_STRUCT(tstatsvec
,struct tstatstruct
);
543 static void onionready(char *sname
, const u8
*secret
, const u8
*pubonion
)
550 if (numneedgenerate
) {
551 pthread_mutex_lock(&keysgenerated_mutex
);
552 if (keysgenerated
>= numneedgenerate
) {
553 pthread_mutex_unlock(&keysgenerated_mutex
);
558 if (mkdir(sname
,0700) != 0) {
560 pthread_mutex_unlock(&keysgenerated_mutex
);
564 if (numneedgenerate
) {
566 if (keysgenerated
>= numneedgenerate
)
568 pthread_mutex_unlock(&keysgenerated_mutex
);
571 strcpy(&sname
[onionendpos
], "/hs_ed25519_secret_key");
572 fh
= fopen(sname
, "wb");
574 fwrite(secret
, skprefixlen
+ SECRET_LEN
, 1, fh
);
578 strcpy(&sname
[onionendpos
], "/hostname");
579 fh
= fopen(sname
, "w");
581 sname
[onionendpos
] = '\n';
582 fwrite(&sname
[direndpos
], ONIONLEN
+1, 1, fh
);
586 strcpy(&sname
[onionendpos
], "/hs_ed25519_public_key");
587 fh
= fopen(sname
, "wb");
589 fwrite(pubonion
, pkprefixlen
+ PUBLIC_LEN
, 1, fh
);
594 sname
[onionendpos
] = '\n';
595 pthread_mutex_lock(&fout_mutex
);
596 fwrite(&sname
[printstartpos
], printlen
, 1, fout
);
598 pthread_mutex_unlock(&fout_mutex
);
603 static void addseed(u8
*seed
)
605 register unsigned int c
= 1;
606 for (size_t i
= 0;i
< SEED_LEN
;++i
) {
607 c
= (unsigned int)seed
[i
] + c
; seed
[i
] = c
& 0xFF; c
>>= 8;
613 static void *dowork(void *task
)
615 union pubonionunion
{
616 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
623 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
624 u8 secret
[skprefixlen
+ SECRET_LEN
];
625 u8
* const sk
= &secret
[skprefixlen
];
627 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
631 struct statstruct
*st
= (struct statstruct
*)task
;
634 memcpy(secret
,skprefix
,skprefixlen
);
635 memcpy(pubonion
.raw
,pkprefix
,pkprefixlen
);
636 // write version later as it will be overwritten by hash
637 memcpy(hashsrc
,checksumstr
,checksumstrlen
);
638 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
640 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
642 memcpy(sname
,workdir
,workdirlen
);
645 randombytes(seed
,sizeof(seed
));
648 if (unlikely(endwork
))
651 ed25519_seckey_expand(sk
,seed
);
652 ed25519_pubkey(pk
,sk
);
659 if (unlikely(MATCHFILTER(i
,pk
))) {
664 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
665 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
667 pk
[PUBLIC_LEN
+ 2] = 0x03;
669 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
), ".onion");
670 onionready(sname
, secret
, pubonion
.raw
);
682 static void addu64toscalar32(u8
*dst
,u64 v
)
686 for (i
= 0;i
< 32;++i
) {
687 c
+= *dst
+ (v
& 0xFF); *dst
= c
& 0xFF; c
>>= 8;
693 static void *dofastwork(void *task
)
695 union pubonionunion
{
696 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
703 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
704 u8 secret
[skprefixlen
+ SECRET_LEN
];
705 u8
* const sk
= &secret
[skprefixlen
];
707 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
713 struct statstruct
*st
= (struct statstruct
*)task
;
716 memcpy(secret
, skprefix
, skprefixlen
);
717 memcpy(pubonion
.raw
, pkprefix
, pkprefixlen
);
718 // write version later as it will be overwritten by hash
719 memcpy(hashsrc
, checksumstr
, checksumstrlen
);
720 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
722 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
724 memcpy(sname
, workdir
, workdirlen
);
727 randombytes(seed
,sizeof(seed
));
728 ed25519_seckey_expand(sk
,seed
);
730 ge_scalarmult_base(&ge_public
,sk
);
731 ge_p3_tobytes(pk
,&ge_public
);
733 for (counter
= 0;counter
< U64_MAX
-8;counter
+= 8) {
736 if (unlikely(endwork
))
740 if (unlikely(MATCHFILTER(i
,pk
))) {
742 // update secret key with counter
743 addu64toscalar32(sk
,counter
);
745 if (((sk
[0] & 248) == sk
[0]) && (((sk
[31] & 63) | 64) == sk
[31])) {
746 /* These operations should be a no-op. */
756 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
757 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
759 pk
[PUBLIC_LEN
+ 2] = 0x03;
761 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
),".onion");
762 onionready(sname
,secret
,pubonion
.raw
);
763 // don't reuse same seed
769 ge_add(&sum
, &ge_public
,&ge_eightpoint
);
770 ge_p1p1_to_p3(&ge_public
,&sum
);
771 ge_p3_tobytes(pk
,&ge_public
);
783 void printhelp(const char *progname
)
786 "Usage: %s filter [filter...] [options]\n"
787 " %s -f filterfile [options]\n"
789 "\t-h - print help\n"
790 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
791 "\t-q - do not print diagnostic output to stderr\n"
792 "\t-x - do not print onion names\n"
793 "\t-o filename - output onion names to specified file\n"
794 "\t-F - include directory names in onion names output\n"
795 "\t-d dirname - output directory\n"
796 "\t-t numthreads - specify number of threads (default - auto)\n"
797 "\t-j numthreads - same as -t\n"
798 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
799 "\t-z - use faster key generation method. this is now default\n"
800 "\t-Z - use slower key generation method\n"
801 "\t-s - print statistics each 10 seconds\n"
802 "\t-S t - print statistics every specified ammount of seconds\n"
803 "\t-T - do not reset statistics counters when printing\n"
808 void setworkdir(const char *wd
)
811 size_t l
= strlen(wd
);
816 fprintf(stderr
, "unset workdir\n");
822 char *s
= malloc(l
+ needslash
+ 1);
831 fprintf(stderr
,"set workdir: %s\n",workdir
);
834 VEC_STRUCT(threadvec
, pthread_t
);
836 int main(int argc
,char **argv
)
844 struct threadvec threads
;
846 struct statsvec stats
;
847 struct tstatsvec tstats
;
849 int realtimestats
= 1;
857 pthread_mutex_init(&keysgenerated_mutex
, 0);
858 pthread_mutex_init(&fout_mutex
, 0);
860 const char *progname
= argv
[0];
867 if (!ignoreargs
&& *arg
== '-') {
874 fprintf(stderr
, "unrecognised argument: -\n");
880 else if (!strcmp(arg
, "help"))
883 fprintf(stderr
, "unrecognised argument: --%s\n", arg
);
888 else if (*arg
== 0) {
893 else if (*arg
== 'h')
895 else if (*arg
== 'f') {
897 loadfilterfile(*argv
++);
899 fprintf(stderr
, "additional argument required\n");
903 else if (*arg
== 'q')
905 else if (*arg
== 'x')
907 else if (*arg
== 'o') {
911 fprintf(stderr
, "additional argument required\n");
915 else if (*arg
== 'F')
917 else if (*arg
== 'd') {
922 fprintf(stderr
, "additional argument required\n");
925 else if (*arg
== 't' || *arg
== 'j') {
927 numthreads
= atoi(*argv
++);
929 fprintf(stderr
, "additional argument required\n");
933 else if (*arg
== 'n') {
935 numneedgenerate
= (size_t)atoll(*argv
++);
937 fprintf(stderr
, "additional argument required\n");
941 else if (*arg
== 'Z')
943 else if (*arg
== 'z')
945 else if (*arg
== 's') {
947 reportdelay
= 10000000;
949 fprintf(stderr
,"statistics support not compiled in\n");
953 else if (*arg
== 'S') {
956 reportdelay
= (u64
)atoll(*argv
++) * 1000000;
958 fprintf(stderr
, "additional argument required\n");
962 fprintf(stderr
,"statistics support not compiled in\n");
966 else if (*arg
== 'T') {
970 fprintf(stderr
,"statistics support not compiled in\n");
975 fprintf(stderr
, "unrecognised argument: -%c\n", *arg
);
981 else filters_add(arg
);
987 if (!filters_count())
991 fout
= fopen(outfile
, "w");
994 mkdir(workdir
, 0700);
996 direndpos
= workdirlen
;
997 onionendpos
= workdirlen
+ ONIONLEN
;
1000 printstartpos
= direndpos
;
1001 printlen
= ONIONLEN
+ 1;
1004 printlen
= onionendpos
+ 1;
1007 if (numthreads
<= 0) {
1008 numthreads
= cpucount();
1009 if (numthreads
<= 0)
1013 signal(SIGTERM
,termhandler
);
1014 signal(SIGINT
,termhandler
);
1017 VEC_ADDN(threads
,numthreads
);
1020 VEC_ADDN(stats
,numthreads
);
1023 VEC_ADDN(tstats
,numthreads
);
1027 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
) {
1030 tp
= &VEC_BUF(stats
,i
);
1032 tret
= pthread_create(&VEC_BUF(threads
,i
),0,fastkeygen
? dofastwork
: dowork
,tp
);
1034 fprintf(stderr
,"error while making %dth thread: %d\n",(int)i
,tret
);
1040 struct timespec nowtime
;
1041 u64 istarttime
,inowtime
,ireporttime
= 0,elapsedoffset
= 0;
1042 if (clock_gettime(CLOCK_MONOTONIC
,&nowtime
) < 0) {
1043 fprintf(stderr
, "failed to get time\n");
1046 istarttime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
1049 memset(&ts
,0,sizeof(ts
));
1050 ts
.tv_nsec
= 100000000;
1052 if (numneedgenerate
&& keysgenerated
>= numneedgenerate
) {
1059 clock_gettime(CLOCK_MONOTONIC
,&nowtime
);
1060 inowtime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
1061 u64 sumcalc
= 0,sumsuccess
= 0;
1062 for (size_t i
= 0;i
< numthreads
;++i
) {
1065 newt
= VEC_BUF(stats
,i
).numcalc
.v
;
1066 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumcalc
;
1067 VEC_BUF(tstats
,i
).oldnumcalc
= newt
;
1068 VEC_BUF(tstats
,i
).numcalc
+= (u64
)tdiff
;
1069 sumcalc
+= VEC_BUF(tstats
,i
).numcalc
;
1071 newt
= VEC_BUF(stats
,i
).numsuccess
.v
;
1072 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumsuccess
;
1073 VEC_BUF(tstats
,i
).oldnumsuccess
= newt
;
1074 VEC_BUF(tstats
,i
).numsuccess
+= (u64
)tdiff
;
1075 sumsuccess
+= VEC_BUF(tstats
,i
).numsuccess
;
1077 if (reportdelay
&& (!ireporttime
|| inowtime
- ireporttime
>= reportdelay
)) {
1079 ireporttime
+= reportdelay
;
1081 ireporttime
= inowtime
;
1085 double calcpersec
= (1000000.0 * sumcalc
) / (inowtime
- istarttime
);
1086 double successpersec
= (1000000.0 * sumsuccess
) / (inowtime
- istarttime
);
1087 fprintf(stderr
,">calc/sec:%8lf, success/sec:%8lf, elapsed:%5.6lfsec\n",calcpersec
,successpersec
,(inowtime
- istarttime
+ elapsedoffset
) / 1000000.0);
1089 if (realtimestats
) {
1090 for (size_t i
= 0;i
< numthreads
;++i
) {
1091 VEC_BUF(tstats
,i
).numcalc
= 0;
1092 VEC_BUF(tstats
,i
).numsuccess
= 0;
1094 elapsedoffset
+= inowtime
- istarttime
;
1095 istarttime
= inowtime
;
1098 if (sumcalc
> U64_MAX
/ 2) {
1099 for (size_t i
= 0;i
< numthreads
;++i
) {
1100 VEC_BUF(tstats
,i
).numcalc
/= 2;
1101 VEC_BUF(tstats
,i
).numsuccess
/= 2;
1103 u64 timediff
= (inowtime
- istarttime
+ 1) / 2;
1104 elapsedoffset
+= timediff
;
1105 istarttime
+= timediff
;
1111 fprintf(stderr
, "waiting for threads to finish...\n");
1112 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
)
1113 pthread_join(VEC_BUF(threads
,i
),0);
1115 fprintf(stderr
, "done, quitting\n");
1117 pthread_mutex_destroy(&keysgenerated_mutex
);
1118 pthread_mutex_destroy(&fout_mutex
);