Merge branch 'bc/sha-256'
authorJunio C Hamano <gitster@pobox.com>
Tue, 29 Jan 2019 20:47:55 +0000 (12:47 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 29 Jan 2019 20:47:55 +0000 (12:47 -0800)
Add sha-256 hash and plug it through the code to allow building Git
with the "NewHash".

* bc/sha-256:
hash: add an SHA-256 implementation using OpenSSL
sha256: add an SHA-256 implementation using libgcrypt
Add a base implementation of SHA-256 support
commit-graph: convert to using the_hash_algo
t/helper: add a test helper to compute hash speed
sha1-file: add a constant for hash block size
t: make the sha1 test-tool helper generic
t: add basic tests for our SHA-1 implementation
cache: make hashcmp and hasheq work with larger hashes
hex: introduce functions to print arbitrary hashes
sha1-file: provide functions to look up hash algorithms
sha1-file: rename algorithm to "sha1"

16 files changed:
Makefile
cache.h
commit-graph.c
hash.h
hex.c
sha1-file.c
sha256/block/sha256.c [new file with mode: 0644]
sha256/block/sha256.h [new file with mode: 0644]
sha256/gcrypt.h [new file with mode: 0644]
t/helper/test-hash-speed.c [new file with mode: 0644]
t/helper/test-hash.c [new file with mode: 0644]
t/helper/test-sha1.c
t/helper/test-sha256.c [new file with mode: 0644]
t/helper/test-tool.c
t/helper/test-tool.h
t/t0015-hash.sh [new file with mode: 0755]
index 1a44c811aa56330327172cf693c61f9a221e4e16..6e8d017e8ed04da7a0319400cc7a3f4f9a74e711 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -186,6 +186,12 @@ all::
 # in one call to the platform's SHA1_Update(). e.g. APPLE_COMMON_CRYPTO
 # wants 'SHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L' defined.
 #
+# Define BLK_SHA256 to use the built-in SHA-256 routines.
+#
+# Define GCRYPT_SHA256 to use the SHA-256 routines in libgcrypt.
+#
+# Define OPENSSL_SHA256 to use the SHA-256 routines in OpenSSL.
+#
 # Define NEEDS_CRYPTO_WITH_SSL if you need -lcrypto when using -lssl (Darwin).
 #
 # Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
@@ -724,7 +730,9 @@ TEST_BUILTINS_OBJS += test-dump-split-index.o
 TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
 TEST_BUILTINS_OBJS += test-genrandom.o
+TEST_BUILTINS_OBJS += test-hash.o
 TEST_BUILTINS_OBJS += test-hashmap.o
+TEST_BUILTINS_OBJS += test-hash-speed.o
 TEST_BUILTINS_OBJS += test-index-version.o
 TEST_BUILTINS_OBJS += test-json-writer.o
 TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o
@@ -747,6 +755,7 @@ TEST_BUILTINS_OBJS += test-run-command.o
 TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 TEST_BUILTINS_OBJS += test-sha1.o
 TEST_BUILTINS_OBJS += test-sha1-array.o
+TEST_BUILTINS_OBJS += test-sha256.o
 TEST_BUILTINS_OBJS += test-sigchain.o
 TEST_BUILTINS_OBJS += test-strcmp-offset.o
 TEST_BUILTINS_OBJS += test-string-list.o
@@ -1646,6 +1655,19 @@ endif
 endif
 endif
 
+ifdef OPENSSL_SHA256
+       EXTLIBS += $(LIB_4_CRYPTO)
+       BASIC_CFLAGS += -DSHA256_OPENSSL
+else
+ifdef GCRYPT_SHA256
+       BASIC_CFLAGS += -DSHA256_GCRYPT
+       EXTLIBS += -lgcrypt
+else
+       LIB_OBJS += sha256/block/sha256.o
+       BASIC_CFLAGS += -DSHA256_BLK
+endif
+endif
+
 ifdef SHA1_MAX_BLOCK_SIZE
        LIB_OBJS += compat/sha1-chunked.o
        BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
diff --git a/cache.h b/cache.h
index 49713cc5a5a61bf0749e596b94474f00286dc652..d94eff88a960ce730d8ab5d500b3e89d4c84df9d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -45,10 +45,20 @@ unsigned long git_deflate_bound(git_zstream *, unsigned long);
 /* The length in bytes and in hex digits of an object name (SHA-1 value). */
 #define GIT_SHA1_RAWSZ 20
 #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
+/* The block size of SHA-1. */
+#define GIT_SHA1_BLKSZ 64
+
+/* The length in bytes and in hex digits of an object name (SHA-256 value). */
+#define GIT_SHA256_RAWSZ 32
+#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)
+/* The block size of SHA-256. */
+#define GIT_SHA256_BLKSZ 64
 
 /* The length in byte and in hex digits of the largest possible hash value. */
-#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
-#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
+#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ
+#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ
+/* The largest possible block size for any supported hash. */
+#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ
 
 struct object_id {
        unsigned char hash[GIT_MAX_RAWSZ];
@@ -1028,16 +1038,12 @@ extern const struct object_id null_oid;
 static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
 {
        /*
-        * This is a temporary optimization hack. By asserting the size here,
-        * we let the compiler know that it's always going to be 20, which lets
-        * it turn this fixed-size memcmp into a few inline instructions.
-        *
-        * This will need to be extended or ripped out when we learn about
-        * hashes of different sizes.
+        * Teach the compiler that there are only two possibilities of hash size
+        * here, so that it can optimize for this case as much as possible.
         */
-       if (the_hash_algo->rawsz != 20)
-               BUG("hash size not yet supported by hashcmp");
-       return memcmp(sha1, sha2, the_hash_algo->rawsz);
+       if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
+               return memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+       return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
 }
 
 static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
@@ -1047,7 +1053,13 @@ static inline int oidcmp(const struct object_id *oid1, const struct object_id *o
 
 static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
 {
-       return !hashcmp(sha1, sha2);
+       /*
+        * We write this here instead of deferring to hashcmp so that the
+        * compiler can properly inline it and avoid calling memcmp.
+        */
+       if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)
+               return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);
+       return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
 }
 
 static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
@@ -1365,9 +1377,9 @@ extern int get_oid_hex(const char *hex, struct object_id *sha1);
 extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 
 /*
- * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
+ * Convert a binary hash to its hex equivalent. The `_r` variant is reentrant,
  * and writes the NUL-terminated output to the buffer `out`, which must be at
- * least `GIT_SHA1_HEXSZ + 1` bytes, and returns a pointer to out for
+ * least `GIT_MAX_HEXSZ + 1` bytes, and returns a pointer to out for
  * convenience.
  *
  * The non-`_r` variant returns a static buffer, but uses a ring of 4
@@ -1375,10 +1387,13 @@ extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
  *
  *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
  */
-extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
-extern char *oid_to_hex_r(char *out, const struct object_id *oid);
-extern char *sha1_to_hex(const unsigned char *sha1);   /* static buffer result! */
-extern char *oid_to_hex(const struct object_id *oid);  /* same static buffer as sha1_to_hex */
+char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, const struct git_hash_algo *);
+char *sha1_to_hex_r(char *out, const unsigned char *sha1);
+char *oid_to_hex_r(char *out, const struct object_id *oid);
+char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *);      /* static buffer result! */
+char *sha1_to_hex(const unsigned char *sha1);                                          /* same static buffer */
+char *hash_to_hex(const unsigned char *hash);                                          /* same static buffer */
+char *oid_to_hex(const struct object_id *oid);                                         /* same static buffer */
 
 /*
  * Parse a 40-character hexadecimal object ID starting from hex, updating the
index 0d6ba6da2d76fa3a5d43dae74fc1bb7b652c6df0..3f9c03afd57ef1b45a1aa65f648f4cf153aaad96 100644 (file)
 #define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
 #define GRAPH_CHUNKID_LARGEEDGES 0x45444745 /* "EDGE" */
 
-#define GRAPH_DATA_WIDTH 36
+#define GRAPH_DATA_WIDTH (the_hash_algo->rawsz + 16)
 
 #define GRAPH_VERSION_1 0x1
 #define GRAPH_VERSION GRAPH_VERSION_1
 
-#define GRAPH_OID_VERSION_SHA1 1
-#define GRAPH_OID_LEN_SHA1 GIT_SHA1_RAWSZ
-#define GRAPH_OID_VERSION GRAPH_OID_VERSION_SHA1
-#define GRAPH_OID_LEN GRAPH_OID_LEN_SHA1
-
 #define GRAPH_OCTOPUS_EDGES_NEEDED 0x80000000
 #define GRAPH_EDGE_LAST_MASK 0x7fffffff
 #define GRAPH_PARENT_NONE 0x70000000
 #define GRAPH_FANOUT_SIZE (4 * 256)
 #define GRAPH_CHUNKLOOKUP_WIDTH 12
 #define GRAPH_MIN_SIZE (GRAPH_HEADER_SIZE + 4 * GRAPH_CHUNKLOOKUP_WIDTH \
-                       + GRAPH_FANOUT_SIZE + GRAPH_OID_LEN)
+                       + GRAPH_FANOUT_SIZE + the_hash_algo->rawsz)
 
 char *get_commit_graph_filename(const char *obj_dir)
 {
        return xstrfmt("%s/info/commit-graph", obj_dir);
 }
 
+static uint8_t oid_version(void)
+{
+       return 1;
+}
+
 static struct commit_graph *alloc_commit_graph(void)
 {
        struct commit_graph *g = xcalloc(1, sizeof(*g));
@@ -124,15 +124,15 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
        }
 
        hash_version = *(unsigned char*)(data + 5);
-       if (hash_version != GRAPH_OID_VERSION) {
+       if (hash_version != oid_version()) {
                error(_("hash version %X does not match version %X"),
-                     hash_version, GRAPH_OID_VERSION);
+                     hash_version, oid_version());
                goto cleanup_fail;
        }
 
        graph = alloc_commit_graph();
 
-       graph->hash_len = GRAPH_OID_LEN;
+       graph->hash_len = the_hash_algo->rawsz;
        graph->num_chunks = *(unsigned char*)(data + 6);
        graph->graph_fd = fd;
        graph->data = graph_map;
@@ -148,7 +148,7 @@ struct commit_graph *load_commit_graph_one(const char *graph_file)
 
                chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH;
 
-               if (chunk_offset > graph_size - GIT_MAX_RAWSZ) {
+               if (chunk_offset > graph_size - the_hash_algo->rawsz) {
                        error(_("improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
                              (uint32_t)chunk_offset);
                        goto cleanup_fail;
@@ -773,6 +773,7 @@ void write_commit_graph(const char *obj_dir,
        int num_extra_edges;
        struct commit_list *parent;
        struct progress *progress = NULL;
+       const unsigned hashsz = the_hash_algo->rawsz;
 
        if (!commit_graph_compatible(the_repository))
                return;
@@ -918,7 +919,7 @@ void write_commit_graph(const char *obj_dir,
        hashwrite_be32(f, GRAPH_SIGNATURE);
 
        hashwrite_u8(f, GRAPH_VERSION);
-       hashwrite_u8(f, GRAPH_OID_VERSION);
+       hashwrite_u8(f, oid_version());
        hashwrite_u8(f, num_chunks);
        hashwrite_u8(f, 0); /* unused padding byte */
 
@@ -933,8 +934,8 @@ void write_commit_graph(const char *obj_dir,
 
        chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
        chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
-       chunk_offsets[2] = chunk_offsets[1] + GRAPH_OID_LEN * commits.nr;
-       chunk_offsets[3] = chunk_offsets[2] + (GRAPH_OID_LEN + 16) * commits.nr;
+       chunk_offsets[2] = chunk_offsets[1] + hashsz * commits.nr;
+       chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * commits.nr;
        chunk_offsets[4] = chunk_offsets[3] + 4 * num_extra_edges;
 
        for (i = 0; i <= num_chunks; i++) {
@@ -947,8 +948,8 @@ void write_commit_graph(const char *obj_dir,
        }
 
        write_graph_chunk_fanout(f, commits.list, commits.nr);
-       write_graph_chunk_oids(f, GRAPH_OID_LEN, commits.list, commits.nr);
-       write_graph_chunk_data(f, GRAPH_OID_LEN, commits.list, commits.nr);
+       write_graph_chunk_oids(f, hashsz, commits.list, commits.nr);
+       write_graph_chunk_data(f, hashsz, commits.list, commits.nr);
        write_graph_chunk_large_edges(f, commits.list, commits.nr);
 
        close_commit_graph(the_repository);
diff --git a/hash.h b/hash.h
index 7c8238bc2ebfded778351b58c16a3854617082c6..adde708cf26d90ffd7ae10e6cdb8bfd7c08b1db5 100644 (file)
--- a/hash.h
+++ b/hash.h
 #include "block-sha1/sha1.h"
 #endif
 
+#if defined(SHA256_GCRYPT)
+#include "sha256/gcrypt.h"
+#elif defined(SHA256_OPENSSL)
+#include <openssl/sha.h>
+#else
+#include "sha256/block/sha256.h"
+#endif
+
 #ifndef platform_SHA_CTX
 /*
  * platform's underlying implementation of SHA-1; could be OpenSSL,
 #define git_SHA1_Update                platform_SHA1_Update
 #define git_SHA1_Final         platform_SHA1_Final
 
+#ifndef platform_SHA256_CTX
+#define platform_SHA256_CTX    SHA256_CTX
+#define platform_SHA256_Init   SHA256_Init
+#define platform_SHA256_Update SHA256_Update
+#define platform_SHA256_Final  SHA256_Final
+#endif
+
+#define git_SHA256_CTX         platform_SHA256_CTX
+#define git_SHA256_Init                platform_SHA256_Init
+#define git_SHA256_Update      platform_SHA256_Update
+#define git_SHA256_Final       platform_SHA256_Final
+
 #ifdef SHA1_MAX_BLOCK_SIZE
 #include "compat/sha1-chunked.h"
 #undef git_SHA1_Update
 #define GIT_HASH_UNKNOWN 0
 /* SHA-1 */
 #define GIT_HASH_SHA1 1
+/* SHA-256  */
+#define GIT_HASH_SHA256 2
 /* Number of algorithms supported (including unknown). */
-#define GIT_HASH_NALGOS (GIT_HASH_SHA1 + 1)
+#define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)
 
 /* A suitably aligned type for stack allocations of hash contexts. */
 union git_hash_ctx {
        git_SHA_CTX sha1;
+       git_SHA256_CTX sha256;
 };
 typedef union git_hash_ctx git_hash_ctx;
 
@@ -81,6 +104,9 @@ struct git_hash_algo {
        /* The length of the hash in hex characters. */
        size_t hexsz;
 
+       /* The block size of the hash. */
+       size_t blksz;
+
        /* The hash initialization function. */
        git_hash_init_fn init_fn;
 
@@ -98,4 +124,17 @@ struct git_hash_algo {
 };
 extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];
 
+/*
+ * Return a GIT_HASH_* constant based on the name.  Returns GIT_HASH_UNKNOWN if
+ * the name doesn't match a known algorithm.
+ */
+int hash_algo_by_name(const char *name);
+/* Identical, except based on the format ID. */
+int hash_algo_by_id(uint32_t format_id);
+/* Identical, except for a pointer to struct git_hash_algo. */
+static inline int hash_algo_by_ptr(const struct git_hash_algo *p)
+{
+       return p - hash_algos;
+}
+
 #endif
diff --git a/hex.c b/hex.c
index 10af1a29e80f903fb95cbeb71be00e5fdd705e66..7850a8879d5e0a00c0e89e197427cafa23d8e865 100644 (file)
--- a/hex.c
+++ b/hex.c
@@ -73,14 +73,15 @@ int parse_oid_hex(const char *hex, struct object_id *oid, const char **end)
        return ret;
 }
 
-char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
+char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash,
+                         const struct git_hash_algo *algop)
 {
        static const char hex[] = "0123456789abcdef";
        char *buf = buffer;
        int i;
 
-       for (i = 0; i < the_hash_algo->rawsz; i++) {
-               unsigned int val = *sha1++;
+       for (i = 0; i < algop->rawsz; i++) {
+               unsigned int val = *hash++;
                *buf++ = hex[val >> 4];
                *buf++ = hex[val & 0xf];
        }
@@ -89,20 +90,35 @@ char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
        return buffer;
 }
 
+char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
+{
+       return hash_to_hex_algop_r(buffer, sha1, &hash_algos[GIT_HASH_SHA1]);
+}
+
 char *oid_to_hex_r(char *buffer, const struct object_id *oid)
 {
-       return sha1_to_hex_r(buffer, oid->hash);
+       return hash_to_hex_algop_r(buffer, oid->hash, the_hash_algo);
 }
 
-char *sha1_to_hex(const unsigned char *sha1)
+char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *algop)
 {
        static int bufno;
        static char hexbuffer[4][GIT_MAX_HEXSZ + 1];
        bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
-       return sha1_to_hex_r(hexbuffer[bufno], sha1);
+       return hash_to_hex_algop_r(hexbuffer[bufno], hash, algop);
+}
+
+char *sha1_to_hex(const unsigned char *sha1)
+{
+       return hash_to_hex_algop(sha1, &hash_algos[GIT_HASH_SHA1]);
+}
+
+char *hash_to_hex(const unsigned char *hash)
+{
+       return hash_to_hex_algop(hash, the_hash_algo);
 }
 
 char *oid_to_hex(const struct object_id *oid)
 {
-       return sha1_to_hex(oid->hash);
+       return hash_to_hex_algop(oid->hash, the_hash_algo);
 }
index 386b96e1d76622e4650c1bbab90092fef7dd7303..10f9e9936aa0a9e5285ac3efb41af3c939dd0ac7 100644 (file)
 #define EMPTY_TREE_SHA1_BIN_LITERAL \
         "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
         "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
+#define EMPTY_TREE_SHA256_BIN_LITERAL \
+       "\x6e\xf1\x9b\x41\x22\x5c\x53\x69\xf1\xc1" \
+       "\x04\xd4\x5d\x8d\x85\xef\xa9\xb0\x57\xb5" \
+       "\x3b\x14\xb4\xb9\xb9\x39\xdd\x74\xde\xcc" \
+       "\x53\x21"
 
 #define EMPTY_BLOB_SHA1_BIN_LITERAL \
        "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
        "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
+#define EMPTY_BLOB_SHA256_BIN_LITERAL \
+       "\x47\x3a\x0f\x4c\x3b\xe8\xa9\x36\x81\xa2" \
+       "\x67\xe3\xb1\xe9\xa7\xdc\xda\x11\x85\x43" \
+       "\x6f\xe1\x41\xf7\x74\x91\x20\xa3\x03\x72" \
+       "\x18\x13"
 
 const unsigned char null_sha1[GIT_MAX_RAWSZ];
 const struct object_id null_oid;
@@ -53,6 +63,12 @@ static const struct object_id empty_tree_oid = {
 static const struct object_id empty_blob_oid = {
        EMPTY_BLOB_SHA1_BIN_LITERAL
 };
+static const struct object_id empty_tree_oid_sha256 = {
+       EMPTY_TREE_SHA256_BIN_LITERAL
+};
+static const struct object_id empty_blob_oid_sha256 = {
+       EMPTY_BLOB_SHA256_BIN_LITERAL
+};
 
 static void git_hash_sha1_init(git_hash_ctx *ctx)
 {
@@ -69,6 +85,22 @@ static void git_hash_sha1_final(unsigned char *hash, git_hash_ctx *ctx)
        git_SHA1_Final(hash, &ctx->sha1);
 }
 
+
+static void git_hash_sha256_init(git_hash_ctx *ctx)
+{
+       git_SHA256_Init(&ctx->sha256);
+}
+
+static void git_hash_sha256_update(git_hash_ctx *ctx, const void *data, size_t len)
+{
+       git_SHA256_Update(&ctx->sha256, data, len);
+}
+
+static void git_hash_sha256_final(unsigned char *hash, git_hash_ctx *ctx)
+{
+       git_SHA256_Final(hash, &ctx->sha256);
+}
+
 static void git_hash_unknown_init(git_hash_ctx *ctx)
 {
        BUG("trying to init unknown hash");
@@ -90,6 +122,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
                0x00000000,
                0,
                0,
+               0,
                git_hash_unknown_init,
                git_hash_unknown_update,
                git_hash_unknown_final,
@@ -97,17 +130,31 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
                NULL,
        },
        {
-               "sha-1",
+               "sha1",
                /* "sha1", big-endian */
                0x73686131,
                GIT_SHA1_RAWSZ,
                GIT_SHA1_HEXSZ,
+               GIT_SHA1_BLKSZ,
                git_hash_sha1_init,
                git_hash_sha1_update,
                git_hash_sha1_final,
                &empty_tree_oid,
                &empty_blob_oid,
        },
+       {
+               "sha256",
+               /* "s256", big-endian */
+               0x73323536,
+               GIT_SHA256_RAWSZ,
+               GIT_SHA256_HEXSZ,
+               GIT_SHA256_BLKSZ,
+               git_hash_sha256_init,
+               git_hash_sha256_update,
+               git_hash_sha256_final,
+               &empty_tree_oid_sha256,
+               &empty_blob_oid_sha256,
+       }
 };
 
 const char *empty_tree_oid_hex(void)
@@ -122,6 +169,27 @@ const char *empty_blob_oid_hex(void)
        return oid_to_hex_r(buf, the_hash_algo->empty_blob);
 }
 
+int hash_algo_by_name(const char *name)
+{
+       int i;
+       if (!name)
+               return GIT_HASH_UNKNOWN;
+       for (i = 1; i < GIT_HASH_NALGOS; i++)
+               if (!strcmp(name, hash_algos[i].name))
+                       return i;
+       return GIT_HASH_UNKNOWN;
+}
+
+int hash_algo_by_id(uint32_t format_id)
+{
+       int i;
+       for (i = 1; i < GIT_HASH_NALGOS; i++)
+               if (format_id == hash_algos[i].format_id)
+                       return i;
+       return GIT_HASH_UNKNOWN;
+}
+
+
 /*
  * This is meant to hold a *small* number of objects that you would
  * want read_sha1_file() to be able to return, but yet you do not want
diff --git a/sha256/block/sha256.c b/sha256/block/sha256.c
new file mode 100644 (file)
index 0000000..37850b4
--- /dev/null
@@ -0,0 +1,196 @@
+#include "git-compat-util.h"
+#include "./sha256.h"
+
+#undef RND
+#undef BLKSIZE
+
+#define BLKSIZE blk_SHA256_BLKSIZE
+
+void blk_SHA256_Init(blk_SHA256_CTX *ctx)
+{
+       ctx->offset = 0;
+       ctx->size = 0;
+       ctx->state[0] = 0x6a09e667ul;
+       ctx->state[1] = 0xbb67ae85ul;
+       ctx->state[2] = 0x3c6ef372ul;
+       ctx->state[3] = 0xa54ff53aul;
+       ctx->state[4] = 0x510e527ful;
+       ctx->state[5] = 0x9b05688cul;
+       ctx->state[6] = 0x1f83d9abul;
+       ctx->state[7] = 0x5be0cd19ul;
+}
+
+static inline uint32_t ror(uint32_t x, unsigned n)
+{
+       return (x >> n) | (x << (32 - n));
+}
+
+static inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z)
+{
+       return z ^ (x & (y ^ z));
+}
+
+static inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z)
+{
+       return ((x | y) & z) | (x & y);
+}
+
+static inline uint32_t sigma0(uint32_t x)
+{
+       return ror(x, 2) ^ ror(x, 13) ^ ror(x, 22);
+}
+
+static inline uint32_t sigma1(uint32_t x)
+{
+       return ror(x, 6) ^ ror(x, 11) ^ ror(x, 25);
+}
+
+static inline uint32_t gamma0(uint32_t x)
+{
+       return ror(x, 7) ^ ror(x, 18) ^ (x >> 3);
+}
+
+static inline uint32_t gamma1(uint32_t x)
+{
+       return ror(x, 17) ^ ror(x, 19) ^ (x >> 10);
+}
+
+static void blk_SHA256_Transform(blk_SHA256_CTX *ctx, const unsigned char *buf)
+{
+
+       uint32_t S[8], W[64], t0, t1;
+       int i;
+
+       /* copy state into S */
+       for (i = 0; i < 8; i++)
+               S[i] = ctx->state[i];
+
+       /* copy the state into 512-bits into W[0..15] */
+       for (i = 0; i < 16; i++, buf += sizeof(uint32_t))
+               W[i] = get_be32(buf);
+
+       /* fill W[16..63] */
+       for (i = 16; i < 64; i++)
+               W[i] = gamma1(W[i - 2]) + W[i - 7] + gamma0(W[i - 15]) + W[i - 16];
+
+#define RND(a,b,c,d,e,f,g,h,i,ki)                    \
+       t0 = h + sigma1(e) + ch(e, f, g) + ki + W[i];   \
+       t1 = sigma0(a) + maj(a, b, c);                  \
+       d += t0;                                        \
+       h  = t0 + t1;
+
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3);
+       RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee);
+       RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f);
+       RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814);
+       RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208);
+       RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa);
+       RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb);
+       RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7);
+       RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2);
+
+       for (i = 0; i < 8; i++)
+               ctx->state[i] += S[i];
+}
+
+void blk_SHA256_Update(blk_SHA256_CTX *ctx, const void *data, size_t len)
+{
+       unsigned int len_buf = ctx->size & 63;
+
+       ctx->size += len;
+
+       /* Read the data into buf and process blocks as they get full */
+       if (len_buf) {
+               unsigned int left = 64 - len_buf;
+               if (len < left)
+                       left = len;
+               memcpy(len_buf + ctx->buf, data, left);
+               len_buf = (len_buf + left) & 63;
+               len -= left;
+               data = ((const char *)data + left);
+               if (len_buf)
+                       return;
+               blk_SHA256_Transform(ctx, ctx->buf);
+       }
+       while (len >= 64) {
+               blk_SHA256_Transform(ctx, data);
+               data = ((const char *)data + 64);
+               len -= 64;
+       }
+       if (len)
+               memcpy(ctx->buf, data, len);
+}
+
+void blk_SHA256_Final(unsigned char *digest, blk_SHA256_CTX *ctx)
+{
+       static const unsigned char pad[64] = { 0x80 };
+       unsigned int padlen[2];
+       int i;
+
+       /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
+       padlen[0] = htonl((uint32_t)(ctx->size >> 29));
+       padlen[1] = htonl((uint32_t)(ctx->size << 3));
+
+       i = ctx->size & 63;
+       blk_SHA256_Update(ctx, pad, 1 + (63 & (55 - i)));
+       blk_SHA256_Update(ctx, padlen, 8);
+
+       /* copy output */
+       for (i = 0; i < 8; i++, digest += sizeof(uint32_t))
+               put_be32(digest, ctx->state[i]);
+}
diff --git a/sha256/block/sha256.h b/sha256/block/sha256.h
new file mode 100644 (file)
index 0000000..5099d64
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef SHA256_BLOCK_SHA256_H
+#define SHA256_BLOCK_SHA256_H
+
+#define blk_SHA256_BLKSIZE 64
+
+struct blk_SHA256_CTX {
+       uint32_t state[8];
+       uint64_t size;
+       uint32_t offset;
+       uint8_t buf[blk_SHA256_BLKSIZE];
+};
+
+typedef struct blk_SHA256_CTX blk_SHA256_CTX;
+
+void blk_SHA256_Init(blk_SHA256_CTX *ctx);
+void blk_SHA256_Update(blk_SHA256_CTX *ctx, const void *data, size_t len);
+void blk_SHA256_Final(unsigned char *digest, blk_SHA256_CTX *ctx);
+
+#define platform_SHA256_CTX blk_SHA256_CTX
+#define platform_SHA256_Init blk_SHA256_Init
+#define platform_SHA256_Update blk_SHA256_Update
+#define platform_SHA256_Final blk_SHA256_Final
+
+#endif
diff --git a/sha256/gcrypt.h b/sha256/gcrypt.h
new file mode 100644 (file)
index 0000000..09bd8bb
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef SHA256_GCRYPT_H
+#define SHA256_GCRYPT_H
+
+#include <gcrypt.h>
+
+#define SHA256_DIGEST_SIZE 32
+
+typedef gcry_md_hd_t gcrypt_SHA256_CTX;
+
+inline void gcrypt_SHA256_Init(gcrypt_SHA256_CTX *ctx)
+{
+       gcry_md_open(ctx, GCRY_MD_SHA256, 0);
+}
+
+inline void gcrypt_SHA256_Update(gcrypt_SHA256_CTX *ctx, const void *data, size_t len)
+{
+       gcry_md_write(*ctx, data, len);
+}
+
+inline void gcrypt_SHA256_Final(unsigned char *digest, gcrypt_SHA256_CTX *ctx)
+{
+       memcpy(digest, gcry_md_read(*ctx, GCRY_MD_SHA256), SHA256_DIGEST_SIZE);
+}
+
+#define platform_SHA256_CTX gcrypt_SHA256_CTX
+#define platform_SHA256_Init gcrypt_SHA256_Init
+#define platform_SHA256_Update gcrypt_SHA256_Update
+#define platform_SHA256_Final gcrypt_SHA256_Final
+
+#endif
diff --git a/t/helper/test-hash-speed.c b/t/helper/test-hash-speed.c
new file mode 100644 (file)
index 0000000..432233c
--- /dev/null
@@ -0,0 +1,61 @@
+#include "test-tool.h"
+#include "cache.h"
+
+#define NUM_SECONDS 3
+
+static inline void compute_hash(const struct git_hash_algo *algo, git_hash_ctx *ctx, uint8_t *final, const void *p, size_t len)
+{
+       algo->init_fn(ctx);
+       algo->update_fn(ctx, p, len);
+       algo->final_fn(final, ctx);
+}
+
+int cmd__hash_speed(int ac, const char **av)
+{
+       git_hash_ctx ctx;
+       unsigned char hash[GIT_MAX_RAWSZ];
+       clock_t initial, start, end;
+       unsigned bufsizes[] = { 64, 256, 1024, 8192, 16384 };
+       int i;
+       void *p;
+       const struct git_hash_algo *algo = NULL;
+
+       if (ac == 2) {
+               for (i = 1; i < GIT_HASH_NALGOS; i++) {
+                       if (!strcmp(av[1], hash_algos[i].name)) {
+                               algo = &hash_algos[i];
+                               break;
+                       }
+               }
+       }
+       if (!algo)
+               die("usage: test-tool hash-speed algo_name");
+
+       /* Use this as an offset to make overflow less likely. */
+       initial = clock();
+
+       printf("algo: %s\n", algo->name);
+
+       for (i = 0; i < ARRAY_SIZE(bufsizes); i++) {
+               unsigned long j, kb;
+               double kb_per_sec;
+               p = xcalloc(1, bufsizes[i]);
+               start = end = clock() - initial;
+               for (j = 0; ((end - start) / CLOCKS_PER_SEC) < NUM_SECONDS; j++) {
+                       compute_hash(algo, &ctx, hash, p, bufsizes[i]);
+
+                       /*
+                        * Only check elapsed time every 128 iterations to avoid
+                        * dominating the runtime with system calls.
+                        */
+                       if (!(j & 127))
+                               end = clock() - initial;
+               }
+               kb = j * bufsizes[i];
+               kb_per_sec = kb / (1024 * ((double)end - start) / CLOCKS_PER_SEC);
+               printf("size %u: %lu iters; %lu KiB; %0.2f KiB/s\n", bufsizes[i], j, kb, kb_per_sec);
+               free(p);
+       }
+
+       exit(0);
+}
diff --git a/t/helper/test-hash.c b/t/helper/test-hash.c
new file mode 100644 (file)
index 0000000..0a31de6
--- /dev/null
@@ -0,0 +1,58 @@
+#include "test-tool.h"
+#include "cache.h"
+
+int cmd_hash_impl(int ac, const char **av, int algo)
+{
+       git_hash_ctx ctx;
+       unsigned char hash[GIT_MAX_HEXSZ];
+       unsigned bufsz = 8192;
+       int binary = 0;
+       char *buffer;
+       const struct git_hash_algo *algop = &hash_algos[algo];
+
+       if (ac == 2) {
+               if (!strcmp(av[1], "-b"))
+                       binary = 1;
+               else
+                       bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024;
+       }
+
+       if (!bufsz)
+               bufsz = 8192;
+
+       while ((buffer = malloc(bufsz)) == NULL) {
+               fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz);
+               bufsz /= 2;
+               if (bufsz < 1024)
+                       die("OOPS");
+       }
+
+       algop->init_fn(&ctx);
+
+       while (1) {
+               ssize_t sz, this_sz;
+               char *cp = buffer;
+               unsigned room = bufsz;
+               this_sz = 0;
+               while (room) {
+                       sz = xread(0, cp, room);
+                       if (sz == 0)
+                               break;
+                       if (sz < 0)
+                               die_errno("test-hash");
+                       this_sz += sz;
+                       cp += sz;
+                       room -= sz;
+               }
+               if (this_sz == 0)
+                       break;
+               algop->update_fn(&ctx, buffer, this_sz);
+       }
+       algop->final_fn(hash, &ctx);
+
+       if (binary)
+               fwrite(hash, 1, algop->rawsz, stdout);
+       else
+               puts(hash_to_hex_algop(hash, algop));
+       exit(0);
+}
index 1ba0675c75f0d2dab281d054b577272cd45c39f9..d860c387c3846d69b7bd63a144ede2f93da60886 100644 (file)
@@ -3,55 +3,5 @@
 
 int cmd__sha1(int ac, const char **av)
 {
-       git_SHA_CTX ctx;
-       unsigned char sha1[20];
-       unsigned bufsz = 8192;
-       int binary = 0;
-       char *buffer;
-
-       if (ac == 2) {
-               if (!strcmp(av[1], "-b"))
-                       binary = 1;
-               else
-                       bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024;
-       }
-
-       if (!bufsz)
-               bufsz = 8192;
-
-       while ((buffer = malloc(bufsz)) == NULL) {
-               fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz);
-               bufsz /= 2;
-               if (bufsz < 1024)
-                       die("OOPS");
-       }
-
-       git_SHA1_Init(&ctx);
-
-       while (1) {
-               ssize_t sz, this_sz;
-               char *cp = buffer;
-               unsigned room = bufsz;
-               this_sz = 0;
-               while (room) {
-                       sz = xread(0, cp, room);
-                       if (sz == 0)
-                               break;
-                       if (sz < 0)
-                               die_errno("test-sha1");
-                       this_sz += sz;
-                       cp += sz;
-                       room -= sz;
-               }
-               if (this_sz == 0)
-                       break;
-               git_SHA1_Update(&ctx, buffer, this_sz);
-       }
-       git_SHA1_Final(sha1, &ctx);
-
-       if (binary)
-               fwrite(sha1, 1, 20, stdout);
-       else
-               puts(sha1_to_hex(sha1));
-       exit(0);
+       return cmd_hash_impl(ac, av, GIT_HASH_SHA1);
 }
diff --git a/t/helper/test-sha256.c b/t/helper/test-sha256.c
new file mode 100644 (file)
index 0000000..0ac6a99
--- /dev/null
@@ -0,0 +1,7 @@
+#include "test-tool.h"
+#include "cache.h"
+
+int cmd__sha256(int ac, const char **av)
+{
+       return cmd_hash_impl(ac, av, GIT_HASH_SHA256);
+}
index bfb195b1a828a34988912e66f9e07f493588d5b4..5b137874e1d21166c92d00f540f6ecd68c18780a 100644 (file)
@@ -20,6 +20,7 @@ static struct test_cmd cmds[] = {
        { "example-decorate", cmd__example_decorate },
        { "genrandom", cmd__genrandom },
        { "hashmap", cmd__hashmap },
+       { "hash-speed", cmd__hash_speed },
        { "index-version", cmd__index_version },
        { "json-writer", cmd__json_writer },
        { "lazy-init-name-hash", cmd__lazy_init_name_hash },
@@ -42,6 +43,7 @@ static struct test_cmd cmds[] = {
        { "scrap-cache-tree", cmd__scrap_cache_tree },
        { "sha1", cmd__sha1 },
        { "sha1-array", cmd__sha1_array },
+       { "sha256", cmd__sha256 },
        { "sigchain", cmd__sigchain },
        { "strcmp-offset", cmd__strcmp_offset },
        { "string-list", cmd__string_list },
index 042f12464b2a17afeb37e01d90bf49ba33ce879a..ca5c88edb2ac8ae99090ec21e6b3d636bc03e96c 100644 (file)
@@ -16,6 +16,7 @@ int cmd__dump_untracked_cache(int argc, const char **argv);
 int cmd__example_decorate(int argc, const char **argv);
 int cmd__genrandom(int argc, const char **argv);
 int cmd__hashmap(int argc, const char **argv);
+int cmd__hash_speed(int argc, const char **argv);
 int cmd__index_version(int argc, const char **argv);
 int cmd__json_writer(int argc, const char **argv);
 int cmd__lazy_init_name_hash(int argc, const char **argv);
@@ -38,6 +39,7 @@ int cmd__run_command(int argc, const char **argv);
 int cmd__scrap_cache_tree(int argc, const char **argv);
 int cmd__sha1(int argc, const char **argv);
 int cmd__sha1_array(int argc, const char **argv);
+int cmd__sha256(int argc, const char **argv);
 int cmd__sigchain(int argc, const char **argv);
 int cmd__strcmp_offset(int argc, const char **argv);
 int cmd__string_list(int argc, const char **argv);
@@ -51,4 +53,6 @@ int cmd__windows_named_pipe(int argc, const char **argv);
 #endif
 int cmd__write_cache(int argc, const char **argv);
 
+int cmd_hash_impl(int ac, const char **av, int algo);
+
 #endif
diff --git a/t/t0015-hash.sh b/t/t0015-hash.sh
new file mode 100755 (executable)
index 0000000..291e906
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='test basic hash implementation'
+. ./test-lib.sh
+
+
+test_expect_success 'test basic SHA-1 hash values' '
+       test-tool sha1 </dev/null >actual &&
+       grep da39a3ee5e6b4b0d3255bfef95601890afd80709 actual &&
+       printf "a" | test-tool sha1 >actual &&
+       grep 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 actual &&
+       printf "abc" | test-tool sha1 >actual &&
+       grep a9993e364706816aba3e25717850c26c9cd0d89d actual &&
+       printf "message digest" | test-tool sha1 >actual &&
+       grep c12252ceda8be8994d5fa0290a47231c1d16aae3 actual &&
+       printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha1 >actual &&
+       grep 32d10c7b8cf96570ca04ce37f2a19d84240d3a89 actual &&
+       perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \
+               test-tool sha1 >actual &&
+       grep 34aa973cd4c4daa4f61eeb2bdbad27316534016f actual &&
+       printf "blob 0\0" | test-tool sha1 >actual &&
+       grep e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 actual &&
+       printf "blob 3\0abc" | test-tool sha1 >actual &&
+       grep f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f actual &&
+       printf "tree 0\0" | test-tool sha1 >actual &&
+       grep 4b825dc642cb6eb9a060e54bf8d69288fbee4904 actual
+'
+
+test_expect_success 'test basic SHA-256 hash values' '
+       test-tool sha256 </dev/null >actual &&
+       grep e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 actual &&
+       printf "a" | test-tool sha256 >actual &&
+       grep ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb actual &&
+       printf "abc" | test-tool sha256 >actual &&
+       grep ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad actual &&
+       printf "message digest" | test-tool sha256 >actual &&
+       grep f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650 actual &&
+       printf "abcdefghijklmnopqrstuvwxyz" | test-tool sha256 >actual &&
+       grep 71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73 actual &&
+       # Try to exercise the chunking code by turning autoflush on.
+       perl -e "$| = 1; print q{aaaaaaaaaa} for 1..100000;" | \
+               test-tool sha256 >actual &&
+       grep cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0 actual &&
+       perl -e "$| = 1; print q{abcdefghijklmnopqrstuvwxyz} for 1..100000;" | \
+               test-tool sha256 >actual &&
+       grep e406ba321ca712ad35a698bf0af8d61fc4dc40eca6bdcea4697962724ccbde35 actual &&
+       printf "blob 0\0" | test-tool sha256 >actual &&
+       grep 473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 actual &&
+       printf "blob 3\0abc" | test-tool sha256 >actual &&
+       grep c1cf6e465077930e88dc5136641d402f72a229ddd996f627d60e9639eaba35a6 actual &&
+       printf "tree 0\0" | test-tool sha256 >actual &&
+       grep 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321 actual
+'
+
+test_done