Skip to content

Commit

Permalink
Merge pull request #2 from exussum12/addFFI
Browse files Browse the repository at this point in the history
FFI Support
  • Loading branch information
exussum12 authored Jun 22, 2020
2 parents 1cd487c + 9b8ed9c commit 929ddc5
Show file tree
Hide file tree
Showing 13 changed files with 315 additions and 19 deletions.
37 changes: 27 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,53 @@
A pure PHP implementation of [xxhash](https://github.com/Cyan4973/xxHash)

Currently only working for the 32 bit version.
Currently only working for the 32 bit version. (Pre PHP 7.4). 32 and 64 bit version both work with PHP 7.4

If speed is important use the php extension instead
If speed is important use the FFI versions (PHP 7.4+)

XXHash is a fast hash designed for file integrity checking. Passwords should not be hashed with this, please use Argon2 or BCrypt.

# Installing

Ideally use composer
With composer

composer require exussum12/xxhash

Alternatively require the single file (making sure the path is correct)

require 'xxhash/V32.php';

# Hashing input

xxhash has a seed, this is 0 by default. To make a new instance of xxhash run

use exussum12\xxhash\V32;
$seed = 0;
$hash = new V32($seed);

Then to hash input, run

$hash->hash('string'); ## to hasha string
$hash->hash('string'); ## to hash a string

or

$file = fopen('path/to/file.ext', 'r');
$hash->hashStream($file); # for a stream (better for large files)

The library can be called statically also, however this removes the ability to change the seed. The default see of 0 will be used

V32::hash('string'); ## to hasha string
V32::hash('string'); ## to hash a string
$file = fopen('path/to/file.ext', 'r');
V32::hashStream($file); # for a stream (better for large files)

Static functions should in general be avoided so the first method is the preferred method to use.
Static functions should in general be avoided, so the first method is the preferred method to use.

## FFI
Since PHP 7.4 FFI allows PHP to call the native C client. This is much faster, and the preferred way if your running PHP 7.4

### Speed Comparison
This is hashing a 320mb file using the stream method.
The time is the time take (smaller is better)

|Method |Time | Peak Memory |
|-------------|------|-------------|
|xxHash Binary| 0.081| 1604kb|
|FFI | 0.194| 27616kb|
|Pure PHP |49.218| 27844kb|

Memory measured using `/usr/bin/time -v`
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"type": "library",
"require": {
"phpunit/phpunit": "^7.1",
"php": ">=7.1",
"php": ">=7.4",
"ext-bcmath": "*"
},
"autoload": {
Expand Down
55 changes: 55 additions & 0 deletions src/Ffi/Base.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
namespace exussum12\xxhash\Ffi;

use FFI;
use exussum12\xxhash\Hash;

/**
* Class V64
* This uses the FFI extension which should be much faster than native.
* @see https://github.com/Cyan4973/xxHash/wiki/xxHash-specification-(draft)
*/
abstract class Base implements Hash
{
protected int $seed;
protected FFI $ffi;

//functions from below
protected string $hash = '';
protected string $createState = '';
protected string $reset = '';
protected string $update = '';
protected string $digest = '';
protected string $freeState = '';

public function hash(string $input): string
{
if (!isset($this)) {
$hash = new static();
return $hash->hash($input);
}

return dechex($this->ffi->{$this->hash}($input, strlen($input), $this->seed));
}

public function hashStream($input): string
{
if (!isset($this)) {
$hash = new static();
return $hash->hashStream($input);
}

$state = $this->ffi->{$this->createState}();
$this->ffi->{$this->reset}($state, $this->seed);

while (!feof($input)) {
$buffer = fread($input, 8192);
$this->ffi->{$this->update}($state, $buffer, strlen($buffer));
}

$hash = dechex($this->ffi->{$this->digest}($state));

$this->ffi->{$this->freeState}($state);
return $hash;
}
}
17 changes: 17 additions & 0 deletions src/Ffi/Headers/XXH32.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
typedef uint32_t XXH32_hash_t;
typedef struct XXH32_state_s XXH32_state_t;
typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
XXH32_hash_t XXH32(
const void* input,
size_t length,
XXH32_hash_t seed
);
void XXH32_update(
XXH32_state_t* state,
const void* input,
size_t len
);
XXH32_state_t* XXH32_createState();
void XXH32_reset(XXH32_state_t*, XXH32_hash_t);
XXH32_hash_t XXH32_digest(XXH32_state_t*);
XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr);
17 changes: 17 additions & 0 deletions src/Ffi/Headers/XXH64.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
typedef uint64_t XXH64_hash_t;
typedef struct XXH64_state_s XXH64_state_t;
typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
XXH64_hash_t XXH64(
const void* input,
size_t length,
XXH64_hash_t seed
);
void XXH64_update(
XXH64_state_t* state,
const void* input,
size_t len
);
XXH64_state_t* XXH64_createState();
void XXH64_reset(XXH64_state_t*, XXH64_hash_t);
XXH64_hash_t XXH64_digest(XXH64_state_t*);
XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr);
28 changes: 28 additions & 0 deletions src/Ffi/V32.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
namespace exussum12\xxhash\Ffi;

use FFI;

/**
* Class V32
* This uses the FFI extension which should be much faster than native.
* @see https://github.com/Cyan4973/xxHash/wiki/xxHash-specification-(draft)
*/
final class V32 extends Base
{
protected string $hash = 'XXH32';
protected string $createState = 'XXH32_createState';
protected string $reset = 'XXH32_reset';
protected string $update = 'XXH32_update';
protected string $digest = 'XXH32_digest';
protected string $freeState = 'XXH32_digest';

public function __construct(int $seed = 0)
{
$this->seed = $seed;
$this->ffi = FFI::cdef(
file_get_contents(__DIR__ . '/Headers/XXH32.h'),
__DIR__ . '/libxxhash.so.0.7.4'
);
}
}
28 changes: 28 additions & 0 deletions src/Ffi/V64.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php
namespace exussum12\xxhash\Ffi;

use FFI;

/**
* Class V64
* This uses the FFI extension which should be much faster than native.
* @see https://github.com/Cyan4973/xxHash/wiki/xxHash-specification-(draft)
*/
final class V64 extends Base
{
protected string $hash = 'XXH64';
protected string $createState = 'XXH64_createState';
protected string $reset = 'XXH64_reset';
protected string $update = 'XXH64_update';
protected string $digest = 'XXH64_digest';
protected string $freeState = 'XXH64_digest';

public function __construct(int $seed = 0)
{
$this->seed = $seed;
$this->ffi = FFI::cdef(
file_get_contents(__DIR__ . '/Headers/XXH64.h'),
__DIR__ . '/libxxhash.so.0.7.4'
);
}
}
Binary file added src/Ffi/libxxhash.so.0.7.4
Binary file not shown.
10 changes: 10 additions & 0 deletions src/Hash.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace exussum12\xxhash;

interface Hash
{
public function hash(string $input): string;

public function hashStream($input): string;
}
9 changes: 5 additions & 4 deletions src/V32.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<?php
namespace exussum12\xxhash;

use InvalidArgumentException;

/**
* Class V32
* @see https://github.com/Cyan4973/xxHash/wiki/xxHash-specification-(draft)
*/
class V32
class V32 implements Hash
{
private const PRIME_1 = 2654435761;
private const PRIME_2 = 2246822519;
Expand Down Expand Up @@ -37,7 +38,7 @@ public function hash(string $input): string

public function hashStream($input): string
{
if (!isset ($this)) {
if (!isset($this)) {
$hash = new self();
return $hash->hashStream($input);
}
Expand Down Expand Up @@ -101,7 +102,7 @@ private function smallInput($input, $accumulator): string
/**
* 32 bit safe multiplication
*/
public function multiply($a, $b):int
public function multiply($a, $b): int
{
$result = $a * $b;
if (is_int($result) && $result > 0) {
Expand All @@ -117,7 +118,7 @@ public function multiply($a, $b):int
/**
* Add but wrap at 32 bits
*/
public function add($a, $b):int
public function add($a, $b): int
{
return ($a + $b) % (1<<32);
}
Expand Down
63 changes: 63 additions & 0 deletions tests/FFIH64HashTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php
namespace exussum12\xxhash\tests;

use PHPUnit\Framework\TestCase;
use exussum12\xxhash\Ffi\V64;

class FFIH64HashTest extends TestCase
{
protected V64 $hash;

public function setUp()
{
$this->hash = new V64(0);
}
public function testSingleByte()
{
$this->assertSame(
'4fdcca5ddb678139',
$this->hash->hash('test')
);
}

public function testLeftOverBytes()
{
$this->assertSame(
'b8f97d6e4b71ad0',
$this->hash->hash('test1')
);
}

public function test16Byes()
{
$this->assertSame(
'539eee07e4f72744',
$this->hash->hash('testtesttesttest')
);
}

public function test17Byes()
{
$this->assertSame(
'bbcf707044ae361a',
$this->hash->hash('testtesttesttest1')
);
}

public function testDifferntSeed()
{
$hash = new V64(3);

$this->assertSame(
'4e75e829de9fa9dd',
$hash->hash('test')
);
}
public function testStaticCall()
{
$this->assertSame(
'4fdcca5ddb678139',
V64::hash('test')
);
}
}
Loading

0 comments on commit 929ddc5

Please sign in to comment.