Skip to content

Commit

Permalink
First Release
Browse files Browse the repository at this point in the history
  • Loading branch information
naqvis committed Apr 22, 2020
0 parents commit 8b2eaab
Show file tree
Hide file tree
Showing 12 changed files with 370 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*.cr]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/docs/
/lib/
/bin/
/.shards/
*.dwarf

# Libraries don't need dependency lock
# Dependencies will be locked in applications that use them
/shard.lock
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
language: crystal

# Uncomment the following if you'd like Travis to run specs and check code formatting
# script:
# - crystal spec
# - crystal tool format --check
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2020 Ali Naqvi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Crystal-FNV

Implements 32, 64 and 128 bits FNV-1 and FNV-1a [Fowler–Noll–Vo hash function](http://www.isthe.com/chongo/tech/comp/fnv/index.html). FNV is a non-cryptographic hash function created by
Glenn Fowler, Landon Curt Noll, and Phong Vo. Refer to [Wikipedia](https://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function) for more details.


## Installation

1. Add the dependency to your `shard.yml`:

```yaml
dependencies:
fnv:
github: naqvis/crystal-fnv
```
2. Run `shards install`

## Usage

```Crystal
require "fnv"
# 32bit FNV-1
digest = Digest::FNV32.digest("foo")
p IO::ByteFormat::BigEndian.decode(UInt32, digest) # => 1083137555
# 32bit FNV-1a
digest = Digest::FNV32A.digest("foo")
p IO::ByteFormat::BigEndian.decode(UInt32, digest) # => 2851307223
# 64bit FNV-1
digest = Digest::FNV64.digest("foo")
p IO::ByteFormat::BigEndian.decode(UInt64, digest) # => 15621798640163566899
# 64bit FNV-1a
digest = Digest::FNV64A.digest("foo")
p IO::ByteFormat::BigEndian.decode(UInt64, digest) # => 15902901984413996407
# 128bit FNV-1
digest = Digest::FNV128.digest("foo")
p IO::ByteFormat::BigEndian.decode(UInt128, digest) # => 221377198890555750482995053501755142603
# 128bit FNV-1a
digest = Digest::FNV128A.digest("foo")
p IO::ByteFormat::BigEndian.decode(UInt128, digest) # => 221385884292107687162785618921601726655
```

## Contributing

1. Fork it (<https://github.com/naqvis/crystal-fnv/fork>)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request

## Contributors

- [Ali Naqvi](https://github.com/naqvis) - creator and maintainer
9 changes: 9 additions & 0 deletions shard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: fnv
version: 0.1.0

authors:
- Ali Naqvi <syed.alinaqvi@gmail.com>

crystal: 0.34.0

license: MIT
4 changes: 4 additions & 0 deletions spec/fnv_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
require "./spec_helper"

describe FNV do
end
2 changes: 2 additions & 0 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require "spec"
require "../src/fnv"
7 changes: 7 additions & 0 deletions src/fnv.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# FNV module implements FNV-1 and FNV-1a, non-cryptographic hash functions created by
# Glenn Fowler, Landon Curt Noll, and Phong Vo.
module FNV
VERSION = "0.1.0"
end

require "./fnv/**"
109 changes: 109 additions & 0 deletions src/fnv/fnv128.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
require "digest/base"

class Digest::FNV128 < Digest::Base
private OFFSET_H = 0x6c62272e07bb0142_u64
private OFFSET_L = 0x62b821756295c58d_u64
private PRIME_L = 0x13b_u64
private PRIME_S = 24_u64

def initialize
@hash = [OFFSET_H, OFFSET_L]
end

def update(data)
data.to_slice.each do |b|
# compute the multiplication
s0, s1 = mul64(PRIME_L, @hash[1])
s0 &+= (@hash[1] << PRIME_S) &+ (PRIME_L &* @hash[0])
# update the values
@hash[1] = s1
@hash[0] = s0
@hash[1] ^= b.to_u64
end
end

def final
end

def result
result = IO::Memory.new
@hash.each do |h|
IO::ByteFormat::BigEndian.encode(h, result)
end
result.rewind
result.to_slice
end

def reset
@hash = [OFFSET_H, OFFSET_L]
end

def sum
@hash
end
end

class Digest::FNV128A < Digest::Base
private OFFSET_H = 0x6c62272e07bb0142_u64
private OFFSET_L = 0x62b821756295c58d_u64
private PRIME_L = 0x13b_u64
private PRIME_S = 24_u64

def initialize
@hash = [OFFSET_H, OFFSET_L]
end

def update(data)
data.to_slice.each do |b|
@hash[1] ^= b.to_u64
# compute the multiplication
s0, s1 = mul64(PRIME_L, @hash[1])
s0 &+= (@hash[1] << PRIME_S) &+ (PRIME_L &* @hash[0])
# update the values
@hash[1] = s1
@hash[0] = s0
end
end

def final
end

def result
result = IO::Memory.new
@hash.each do |h|
IO::ByteFormat::BigEndian.encode(h, result)
end
result.rewind
result.to_slice
end

def reset
@hash = [OFFSET_H, OFFSET_L]
end

def sum
@hash
end
end

private MASK32 = (1_i64 << 32) - 1

# returns the 128-bit product of x and y: (hi, lo) = x * y
# with the product bits' upper half returned in hi and lower
# half returned in lo

# :nodoc:
def self.mul64(x : UInt64, y : UInt64) : {UInt64, UInt64}
x0 = x & MASK32
x1 = (x >> 32)
y0 = y & MASK32
y1 = (y >> 32)
w0 = x0 &* y0
t = x1 &* y0 &+ (w0 >> 32)
w1 = t & MASK32
w2 = (t >> 32)
w1 &+= x0 &* y1
hi = x1 &* y1 &+ w2 &+ (w1 >> 32)
lo = x &* y
{hi, lo}
end
67 changes: 67 additions & 0 deletions src/fnv/fnv32.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
require "digest/base"

class Digest::FNV32 < Digest::Base
private OFFSET = 2166136261_u32
private PRIME = 16777619_u32

def initialize
@hash = OFFSET
end

def update(data)
data.to_slice.each do |b|
@hash &*= PRIME
@hash ^= b.to_u32
end
end

def final
end

def result
result = Bytes.new(sizeof(UInt32))
IO::ByteFormat::BigEndian.encode(@hash, result)
result
end

def reset
@hash = OFFSET
end

def sum : UInt32
@hash
end
end

class Digest::FNV32A < Digest::Base
private OFFSET = 2166136261_u32
private PRIME = 16777619_u32

def initialize
@hash = OFFSET
end

def update(data)
data.to_slice.each do |b|
@hash ^= b.to_u32
@hash &*= PRIME
end
end

def final
end

def result
result = Bytes.new(sizeof(UInt32))
IO::ByteFormat::BigEndian.encode(@hash, result)
result
end

def reset
@hash = OFFSET
end

def sum : UInt32
@hash
end
end
67 changes: 67 additions & 0 deletions src/fnv/fnv64.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
require "digest/base"

class Digest::FNV64 < Digest::Base
private OFFSET = 14695981039346656037_u64
private PRIME = 1099511628211_u64

def initialize
@hash = OFFSET
end

def update(data)
data.to_slice.each do |b|
@hash &*= PRIME
@hash ^= b.to_u64
end
end

def final
end

def result
result = Bytes.new(sizeof(typeof(@hash)))
IO::ByteFormat::BigEndian.encode(@hash, result)
result
end

def reset
@hash = OFFSET
end

def sum : UInt64
@hash
end
end

class Digest::FNV64A < Digest::Base
private OFFSET = 14695981039346656037_u64
private PRIME = 1099511628211_u64

def initialize
@hash = OFFSET
end

def update(data)
data.to_slice.each do |b|
@hash ^= b.to_u64
@hash &*= PRIME
end
end

def final
end

def result
result = Bytes.new(sizeof(typeof(@hash)))
IO::ByteFormat::BigEndian.encode(@hash, result)
result
end

def reset
@hash = OFFSET
end

def sum : UInt64
@hash
end
end

0 comments on commit 8b2eaab

Please sign in to comment.