Skip to content
This repository has been archived by the owner on Mar 26, 2024. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-schievink committed Jun 25, 2020
0 parents commit 8fe4c7e
Show file tree
Hide file tree
Showing 10 changed files with 308 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
Cargo.lock
19 changes: 19 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
language: rust
rust:
- 1.31.0 # TODO: Adjust minimum supported Rust version accordingly
- stable
- nightly
cache: cargo
sudo: false
env:
global:
- RUSTFLAGS="--deny warnings"
- RUST_BACKTRACE=1
- CARGO_INCREMENTAL=0 # decrease size of `target` to make the cache smaller
matrix:
- FEATURES="" # default configuration
script:
- cargo test --all $FEATURES
notifications:
email:
on_success: never
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## Unreleased

Initial release.
54 changes: 54 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
[package]
name = "adler"
version = "0.0.0"
authors = ["Jonas Schievink <[email protected]>"]
edition = "2018"
description = "A simple clean-room implementation of the Adler-32 checksum"
documentation = "https://docs.rs/adler/"
repository = "https://github.com/jonas-schievink/adler.git"
keywords = ["checksum", "integrity", "hash", "adler32"]
categories = ["algorithms"]
readme = "README.md"
license = "0BSD"

[badges]
travis-ci = { repository = "jonas-schievink/adler" }
maintenance = { status = "actively-developed" }

[[bench]]
name = "bench"
harness = false

[dev-dependencies]
criterion = "0.3.2"

# cargo-release configuration
[package.metadata.release]
tag-message = "{{version}}"
no-dev-version = true
pre-release-commit-message = "Release {{version}}"

# Change the changelog's `Unreleased` section to refer to this release and
# prepend a new `Unreleased` section
[[package.metadata.release.pre-release-replacements]]
file = "CHANGELOG.md"
search = "## Unreleased\n"
replace = """
## Unreleased
No changes.
## [{{version}} - {{date}}](https://github.com/jonas-schievink/adler/releases/tag/{{version}})
"""

# Bump the version inside the example manifest in `README.md`
[[package.metadata.release.pre-release-replacements]]
file = "README.md"
search = 'adler = "[a-z0-9\\.-]+"'
replace = 'adler = "{{version}}"'

# Bump the version referenced by the `html_root_url` attribute in `lib.rs`
[[package.metadata.release.pre-release-replacements]]
file = "src/lib.rs"
search = "https://docs.rs/adler/[a-z0-9\\.-]+"
replace = "https://docs.rs/adler/{{version}}"
12 changes: 12 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Copyright (C) Jonas Schievink <[email protected]>

Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Adler-32 checksums

[![crates.io](https://img.shields.io/crates/v/adler.svg)](https://crates.io/crates/adler)
[![docs.rs](https://docs.rs/adler/badge.svg)](https://docs.rs/adler/)
[![Build Status](https://travis-ci.org/jonas-schievink/adler.svg?branch=master)](https://travis-ci.org/jonas-schievink/adler)

This crate provides a simple implementation of the Adler-32 checksum, used in
zlib, rsync, and other software.

Please refer to the [changelog](CHANGELOG.md) to see what changed in the last
releases.

## Features

- Permissively licensed clean-room implementation.
- Zero dependencies.

## Usage

Add an entry to your `Cargo.toml`:

```toml
[dependencies]
adler = "0.0.0"
```

Check the [API Documentation](https://docs.rs/adler/) for how to use the
crate's functionality.

## Rust version support

This crate supports the 3 latest stable Rust releases. Bumping the minimum
supported Rust version (MSRV) is not considered a breaking change as long as
these 3 versions are still supported.

The MSRV is also explicitly tested against in [.travis.yml](.travis.yml).
13 changes: 13 additions & 0 deletions RELEASE_PROCESS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# What to do to publish a new release

1. Ensure all notable changes are in the changelog under "Unreleased".

2. Execute `cargo release <level>` to bump version(s), tag and publish
everything. External subcommand, must be installed with `cargo install
cargo-release`.

`<level>` can be one of `major|minor|patch`. If this is the first release
(`0.1.0`), use `minor`, since the version starts out as `0.0.0`.

3. Go to the GitHub releases, edit the just-pushed tag. Copy the release notes
from the changelog.
60 changes: 60 additions & 0 deletions benches/bench.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use criterion::{criterion_group, criterion_main, Criterion, Throughput};

fn bench(c: &mut Criterion) {
{
const SIZE: usize = 100;

let mut group = c.benchmark_group("100b");
group.throughput(Throughput::Bytes(SIZE as u64));
group.bench_function("zeroes-100", |bencher| {
bencher.iter(|| {
adler::from_slice(&[0; SIZE]);
});
});
group.bench_function("ones-100", |bencher| {
bencher.iter(|| {
adler::from_slice(&[0xff; SIZE]);
});
});
}

{
const SIZE: usize = 1024;

let mut group = c.benchmark_group("1k");
group.throughput(Throughput::Bytes(SIZE as u64));

group.bench_function("zeroes-1k", |bencher| {
bencher.iter(|| {
adler::from_slice(&[0; SIZE]);
});
});

group.bench_function("ones-1k", |bencher| {
bencher.iter(|| {
adler::from_slice(&[0xff; SIZE]);
});
});
}

{
const SIZE: usize = 1024 * 1024;

let mut group = c.benchmark_group("1m");
group.throughput(Throughput::Bytes(SIZE as u64));
group.bench_function("zeroes-1m", |bencher| {
bencher.iter(|| {
adler::from_slice(&[0; SIZE]);
});
});

group.bench_function("ones-1m", |bencher| {
bencher.iter(|| {
adler::from_slice(&[0xff; SIZE]);
});
});
}
}

criterion_group!(benches, bench);
criterion_main!(benches);
97 changes: 97 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//! TODO: Write crate docs
#![doc(html_root_url = "https://docs.rs/adler/0.0.0")]
// Deny a few warnings in doctests, since rustdoc `allow`s many warnings by default
#![doc(test(attr(deny(unused_imports, unused_must_use))))]
#![warn(missing_debug_implementations, rust_2018_idioms)]

mod readme;

use core::hash::Hasher;

const MOD: u32 = 65521;

/// Adler-32 checksum calculator.
#[derive(Debug, Copy, Clone)]
pub struct Adler32 {
a: u16,
b: u16,
}

impl Adler32 {
/// Creates a new Adler-32 instance.
pub fn new() -> Self {
Self::default()
}

/// Returns the calculated checksum at this point in time.
pub fn finish(&self) -> u32 {
(u32::from(self.b) << 16) | u32::from(self.a)
}
}

impl Default for Adler32 {
fn default() -> Self {
Self { a: 1, b: 0 }
}
}

impl Hasher for Adler32 {
fn finish(&self) -> u64 {
u64::from(self.finish())
}

fn write(&mut self, bytes: &[u8]) {
for byte in bytes {
let val = u32::from(*byte);
let a = u32::from(self.a);
let b = u32::from(self.b);
let new_a = (a + val) % MOD;
let new_b = (b + new_a) % MOD;
self.a = new_a as u16;
self.b = new_b as u16;
}
}
}

/// Calculates the Adler-32 checksum of a byte slice.
pub fn from_slice(data: &[u8]) -> u32 {
let mut h = Adler32::new();
h.write(data);
h.finish()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn zeroes() {
assert_eq!(from_slice(&[]), 1);
assert_eq!(from_slice(&[0]), 1 | 1 << 16);
assert_eq!(from_slice(&[0, 0]), 1 | 2 << 16);
assert_eq!(from_slice(&[0; 100]), 0x00640001);
assert_eq!(from_slice(&[0; 1024]), 0x04000001);
assert_eq!(from_slice(&[0; 1024 * 1024]), 0x00f00001);
}

#[test]
fn ones() {
assert_eq!(from_slice(&[0xff; 1024]), 0x79a6fc2e);
assert_eq!(from_slice(&[0xff; 1024 * 1024]), 0x8e88ef11);
}

#[test]
fn mixed() {
assert_eq!(from_slice(&[1]), 2 | 2 << 16);
assert_eq!(from_slice(&[40]), 41 | 41 << 16);

assert_eq!(from_slice(&[0xA5; 1024 * 1024]), 0xd5009ab1);
}

/// Example calculation from https://en.wikipedia.org/wiki/Adler-32.
#[test]
fn wiki() {
assert_eq!(from_slice(b"Wikipedia"), 0x11E60398);
}
}
10 changes: 10 additions & 0 deletions src/readme.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//! Includes `README.md` as a doc comment so we test examples in it.
macro_rules! doc {
($e:expr) => {
#[doc = $e]
extern {}
};
}

doc!(include_str!("../README.md"));

0 comments on commit 8fe4c7e

Please sign in to comment.