/**
* Write to an odb stream
*
+ * This method will fail as soon as the total number of
+ * received bytes exceeds the size declared with `git_odb_open_wstream()`
+ *
* @param stream the stream
* @param buffer the data to write
* @param len the buffer's length
* The object will take its final name and will be available to the
* odb.
*
+ * This method will fail if the total number of received bytes
+ * differs from the size declared with `git_odb_open_wstream()`
+ *
* @param out pointer to store the resulting object's id
* @param stream the stream
* @return 0 on success; an error code otherwise
unsigned int mode;
void *hash_ctx;
+ size_t declared_size;
+ size_t received_bytes;
+
/**
* Write at most `len` bytes into `buffer` and advance the
* stream.
* Store the contents of the stream as an object with the id
* specified in `oid`.
*
- * This method will *not* be invoked by libgit2 if the object pointed at
- * by `oid` already exists in any backend. Libgit2 will however take care
- * of properly disposing the stream through a call to `free()`.
+ * This method will *not* be invoked by libgit2 when:
+ * - the object pointed at by `oid` already exists in any backend.
+ * - the total number of received bytes differs from the size declared
+ * with `git_odb_open_wstream()`
+ *
+ * Libgit2 will however take care of properly disposing the stream through
+ * a call to `free()`.
*/
int (*finalize_write)(git_odb_stream *stream, const git_oid *oid);
hash_header(ctx, size, type);
(*stream)->hash_ctx = ctx;
+ (*stream)->declared_size = size;
+ (*stream)->received_bytes = 0;
+
return error;
}
+static int git_odb_stream__invalid_length(
+ const git_odb_stream *stream,
+ const char *action)
+{
+ giterr_set(GITERR_ODB,
+ "Cannot %s - "
+ "Invalid length. %"PRIuZ" was expected. The "
+ "total size of the received chunks amounts to %"PRIuZ".",
+ action, stream->declared_size, stream->received_bytes);
+
+ return -1;
+}
+
int git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len)
{
git_hash_update(stream->hash_ctx, buffer, len);
+
+ stream->received_bytes += len;
+
+ if (stream->received_bytes > stream->declared_size)
+ return git_odb_stream__invalid_length(stream,
+ "stream_write()");
+
return stream->write(stream, buffer, len);
}
int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream)
{
+ if (stream->received_bytes != stream->declared_size)
+ return git_odb_stream__invalid_length(stream,
+ "stream_finalize_write()");
+
git_hash_final(out, stream->hash_ctx);
if (git_odb_exists(stream->backend->odb, out))
--- /dev/null
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+
+static git_repository *repo;
+static git_odb *odb;
+static git_odb_stream *stream;
+
+void test_odb_streamwrite__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_odb(&odb, repo));
+
+ cl_git_pass(git_odb_open_wstream(&stream, odb, 14, GIT_OBJ_BLOB));
+ cl_assert_equal_sz(14, stream->declared_size);
+}
+
+void test_odb_streamwrite__cleanup(void)
+{
+ git_odb_stream_free(stream);
+ git_odb_free(odb);
+ cl_git_sandbox_cleanup();
+}
+
+void test_odb_streamwrite__can_accept_chunks(void)
+{
+ git_oid oid;
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+ cl_assert_equal_sz(8, stream->received_bytes);
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 6));
+ cl_assert_equal_sz(8 + 6, stream->received_bytes);
+
+ cl_git_pass(git_odb_stream_finalize_write(&oid, stream));
+}
+
+void test_odb_streamwrite__can_detect_missing_bytes(void)
+{
+ git_oid oid;
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+ cl_assert_equal_sz(8, stream->received_bytes);
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 4));
+ cl_assert_equal_sz(8 + 4, stream->received_bytes);
+
+ cl_git_fail(git_odb_stream_finalize_write(&oid, stream));
+}
+
+void test_odb_streamwrite__can_detect_additional_bytes(void)
+{
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+ cl_assert_equal_sz(8, stream->received_bytes);
+
+ cl_git_fail(git_odb_stream_write(stream, "deadbeef", 7));
+}