Skip to content

Instantly share code, notes, and snippets.

@sgolemon
Created May 11, 2019 20:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sgolemon/126455b9d32e3ff3c20f7cc42195371a to your computer and use it in GitHub Desktop.
Save sgolemon/126455b9d32e3ff3c20f7cc42195371a to your computer and use it in GitHub Desktop.
GMP via FFI
<?php declare(strict_types=1);
namespace MyGMP {
class Exception extends \Exception {}
} // namespace MyGMP
namespace {
final class MyGMP {
private static ?\FFI $instance = null;
/* @@gmp_randstate_t */
private static ?object $randstate = null;
/* @@mpz_t */
private static ?object $mpz = null;
public function __construct($initialValue = null) {
$instance = self::_getInstance();
$this->mpz = $instance->new('mpz_t');
$instance->__gmpz_init($this->mpz);
if ($initialValue === null) {
$this->setValue(0);
} else {
$this->setValue($initialValue);
}
}
public function setValue($value): self {
$instance = self::_getInstance();
if ($value instanceof \MyGMP) {
$instance->__gmpz_set($this->mpz, $value->mpz);
} elseif (is_int($value)) {
$instance->__gmpz_set_si($this->mpz, $value);
} elseif (is_string($value)) {
$instance->__gmpz_set_str($this->mpz, $value, 0 /* base */);
} else {
throw new \MyGMP\Exception("Invalid type passed to " . __METHOD__ . ": " . gettype($value));
}
return $this;
}
public function getString(int $base = 10): string {
$instance = self::_getInstance();
$len = $instance->__gmpz_sizeinbase($this->mpz, $base) + 2;
$ret = $instance->new("char[$len]");
$instance->__gmpz_get_str($ret, $base, $this->mpz);
$str = '';
for ($i = 0; $i < $len; ++$i) {
if (ord($ret[$i]) == 0) break;
$str .= $ret[$i];
}
return $str;
}
public function add($value): \MyGMP {
if (!($value instanceof \MyGMP)) {
$value = new \MyGMP($value);
}
$ret = new \MyGMP($this);
self::_getInstance()->__gmpz_add($ret->mpz, $this->mpz, $value->mpz);
return $ret;
}
public function __toString(): string {
return $this->getString(10);
}
public function __debugInfo(): array {
return [
2 => $this->getString(2),
8 => $this->getString(8),
10 => $this->getString(10),
16 => $this->getString(16),
];
}
public function __destruct() {
(self::_getInstance())->__gmpz_clear($this->mpz);
$this->mpz = null;
}
public static function getRandom(int $max = PHP_INT_MAX): int {
$instance = self::_getInstance();
if (self::$randstate === null) {
self::$randstate = (self::$instance)->new('gmp_randstate_t');
$instance->__gmp_randinit_mt(self::$randstate);
$instance->__gmp_randseed_ui(self::$randstate, random_int(0, PHP_INT_MAX));
}
return $instance->__gmp_urandomm_ui(self::$randstate, $max);
}
private static function _getInstance(): \FFI {
if (self::$instance) {
return self::$instance;
}
$header = <<<HEADER
typedef unsigned long long int mp_limb_t;
typedef long long int mp_limb_signed_t;
typedef struct {
int _mp_alloc;
int _mp_size;
mp_limb_t *_mp_d;
} __mpz_struct;
typedef __mpz_struct mpz_t[1];
typedef __mpz_struct *mpz_ptr;
typedef const __mpz_struct *mpz_srcptr;
void __gmpz_init(mpz_ptr);
void __gmpz_clear(mpz_ptr);
void __gmpz_set(mpz_ptr, mpz_srcptr);
void __gmpz_set_si(mpz_ptr, signed long int);
int __gmpz_set_str(mpz_ptr, const char *, int);
size_t __gmpz_sizeinbase(mpz_srcptr, int);
char *__gmpz_get_str(char *, int, mpz_srcptr);
void __gmpz_add(mpz_ptr, mpz_srcptr, mpz_srcptr);
typedef enum {
GMP_RAND_ALG_DEFAULT = 0,
GMP_RAND_ALG_LC = GMP_RAND_ALG_DEFAULT,
} gmp_randalg_t;
typedef struct {
mpz_t _mp_seed;
gmp_randalg_t _mp_alg;
union {
void *_mp_lc;
} _mp_algdata;
} __gmp_randstate_struct;
typedef __gmp_randstate_struct gmp_randstate_t[1];
void __gmp_randinit_mt (gmp_randstate_t);
void __gmp_randseed_ui (gmp_randstate_t, unsigned long int);
unsigned long __gmp_urandomm_ui (gmp_randstate_t, unsigned long);
HEADER;
return self::$instance = \FFI::cdef($header, 'libgmp.so');
}
}
} // namespace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment