1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00
putty-source/sshbn.h

61 lines
2.3 KiB
C
Raw Normal View History

/*
* sshbn.h: the assorted conditional definitions of BignumInt and
Rewrite the core divide function to not use DIVMOD_WORD. DIVMOD_WORD is a portability hazard, because implementing it requires either a way to get direct access to the x86 DIV instruction or equivalent (be it inline assembler or a compiler intrinsic), or else an integer type we can use as BignumDblInt. But I'm starting to think about porting to 64-bit Visual Studio with a 64-bit BignumInt, and in that situation neither of those options will be available. I could write a piece of _out_-of-line x86-64 assembler in a separate source file and put a function call in DIVMOD_WORD, but instead I've decided to solve the problem in a more futureproof way: remove DIVMOD_WORD totally and write a division function that doesn't need it at all, solving not only today's porting headache but all future ones in this area. The new implementation works by precomputing (a good enough approximation to) the leading word of the reciprocal of the modulus, and then getting each word of quotient by multiplying by that reciprocal, where we previously used DIVMOD_WORD to divide by the leading word of the actual modulus. The reciprocal itself is computed outside internal_mod() and passed in as a parameter, allowing me to save time by only computing it once when I'm about to do a modpow. To some extent this complicates the implementation: the advantage of DIVMOD_WORD was that it yielded a full word q of quotient every time it was used, so the subtraction of q*m from the input could be done in a nicely word-aligned way. But the reciprocal multiply approach yields _almost_ a full word of quotient, because you have to make the reciprocal a bit short to avoid overflow at multiplication time. For a start, this means we have to do fractionally more iterations of the main loop; but more painfully, we can no longer depend on the subtraction of q*m at every step being word-aligned, and instead we have to be prepared to do it at any bit shift. But the flip side is that once we've implemented that, the rest of the algorithm becomes a lot less full of horrible special cases: in particular, we can now completely throw away the horribleness at all the call sites where we shift the modulus up by a fractional word to set its top bit, and then have to do a little dance to get the last few bits of quotient involving a second call to internal_mod. So there are points both for and against the new implementation in simplicity terms; but I think on balance it's more comprehensible than the old one, and a quick timing test suggests it also ends up a touch faster overall - the new testbn gets through the output of testdata/bignum.py in 4.034s where the old one took 4.392s.
2015-12-13 14:46:43 +00:00
* multiply macros used throughout the bignum code to treat numbers as
* arrays of the most conveniently sized word for the target machine.
* Exported so that other code (e.g. poly1305) can use it too.
*/
#if defined __SIZEOF_INT128__
/* gcc and clang both provide a __uint128_t type on 64-bit targets
* (and, when they do, indicate its presence by the above macro),
* using the same 'two machine registers' kind of code generation that
* 32-bit targets use for 64-bit ints. If we have one of these, we can
* use a 64-bit BignumInt and a 128-bit BignumDblInt. */
typedef unsigned long long BignumInt;
typedef __uint128_t BignumDblInt;
#define BIGNUM_INT_MASK 0xFFFFFFFFFFFFFFFFULL
#define BIGNUM_TOP_BIT 0x8000000000000000ULL
#define BIGNUM_INT_BITS 64
#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
#elif defined __GNUC__ && defined __i386__
typedef unsigned long BignumInt;
typedef unsigned long long BignumDblInt;
#define BIGNUM_INT_MASK 0xFFFFFFFFUL
#define BIGNUM_TOP_BIT 0x80000000UL
#define BIGNUM_INT_BITS 32
#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
#elif defined _MSC_VER && defined _M_IX86
typedef unsigned __int32 BignumInt;
typedef unsigned __int64 BignumDblInt;
#define BIGNUM_INT_MASK 0xFFFFFFFFUL
#define BIGNUM_TOP_BIT 0x80000000UL
#define BIGNUM_INT_BITS 32
#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
#elif defined _LP64
/* 64-bit architectures can do 32x32->64 chunks at a time */
typedef unsigned int BignumInt;
typedef unsigned long BignumDblInt;
#define BIGNUM_INT_MASK 0xFFFFFFFFU
#define BIGNUM_TOP_BIT 0x80000000U
#define BIGNUM_INT_BITS 32
#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
#elif defined _LLP64
/* 64-bit architectures in which unsigned long is 32 bits, not 64 */
typedef unsigned long BignumInt;
typedef unsigned long long BignumDblInt;
#define BIGNUM_INT_MASK 0xFFFFFFFFUL
#define BIGNUM_TOP_BIT 0x80000000UL
#define BIGNUM_INT_BITS 32
#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
#else
/* Fallback for all other cases */
typedef unsigned short BignumInt;
typedef unsigned long BignumDblInt;
#define BIGNUM_INT_MASK 0xFFFFU
#define BIGNUM_TOP_BIT 0x8000U
#define BIGNUM_INT_BITS 16
#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2)
#endif
#define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8)