vcs-svn / svndiff.con commit vcs-svn: read instructions from deltas (ef2ac77)
   1/*
   2 * Licensed under a two-clause BSD-style license.
   3 * See LICENSE for details.
   4 */
   5
   6#include "git-compat-util.h"
   7#include "sliding_window.h"
   8#include "line_buffer.h"
   9#include "svndiff.h"
  10
  11/*
  12 * svndiff0 applier
  13 *
  14 * See http://svn.apache.org/repos/asf/subversion/trunk/notes/svndiff.
  15 *
  16 * svndiff0 ::= 'SVN\0' window*
  17 * window ::= int int int int int instructions inline_data;
  18 * int ::= highdigit* lowdigit;
  19 * highdigit ::= # binary 1000 0000 OR-ed with 7 bit value;
  20 * lowdigit ::= # 7 bit value;
  21 */
  22
  23#define VLI_CONTINUE    0x80
  24#define VLI_DIGIT_MASK  0x7f
  25#define VLI_BITS_PER_DIGIT 7
  26
  27struct window {
  28        struct strbuf instructions;
  29        struct strbuf data;
  30};
  31
  32#define WINDOW_INIT     { STRBUF_INIT, STRBUF_INIT }
  33
  34static void window_release(struct window *ctx)
  35{
  36        strbuf_release(&ctx->instructions);
  37        strbuf_release(&ctx->data);
  38}
  39
  40static int error_short_read(struct line_buffer *input)
  41{
  42        if (buffer_ferror(input))
  43                return error("error reading delta: %s", strerror(errno));
  44        return error("invalid delta: unexpected end of file");
  45}
  46
  47static int read_chunk(struct line_buffer *delta, off_t *delta_len,
  48                      struct strbuf *buf, size_t len)
  49{
  50        strbuf_reset(buf);
  51        if (len > *delta_len ||
  52            buffer_read_binary(delta, buf, len) != len)
  53                return error_short_read(delta);
  54        *delta_len -= buf->len;
  55        return 0;
  56}
  57
  58static int read_magic(struct line_buffer *in, off_t *len)
  59{
  60        static const char magic[] = {'S', 'V', 'N', '\0'};
  61        struct strbuf sb = STRBUF_INIT;
  62
  63        if (read_chunk(in, len, &sb, sizeof(magic))) {
  64                strbuf_release(&sb);
  65                return -1;
  66        }
  67        if (memcmp(sb.buf, magic, sizeof(magic))) {
  68                strbuf_release(&sb);
  69                return error("invalid delta: unrecognized file type");
  70        }
  71        strbuf_release(&sb);
  72        return 0;
  73}
  74
  75static int read_int(struct line_buffer *in, uintmax_t *result, off_t *len)
  76{
  77        uintmax_t rv = 0;
  78        off_t sz;
  79        for (sz = *len; sz; sz--) {
  80                const int ch = buffer_read_char(in);
  81                if (ch == EOF)
  82                        break;
  83
  84                rv <<= VLI_BITS_PER_DIGIT;
  85                rv += (ch & VLI_DIGIT_MASK);
  86                if (ch & VLI_CONTINUE)
  87                        continue;
  88
  89                *result = rv;
  90                *len = sz - 1;
  91                return 0;
  92        }
  93        return error_short_read(in);
  94}
  95
  96static int read_offset(struct line_buffer *in, off_t *result, off_t *len)
  97{
  98        uintmax_t val;
  99        if (read_int(in, &val, len))
 100                return -1;
 101        if (val > maximum_signed_value_of_type(off_t))
 102                return error("unrepresentable offset in delta: %"PRIuMAX"", val);
 103        *result = val;
 104        return 0;
 105}
 106
 107static int read_length(struct line_buffer *in, size_t *result, off_t *len)
 108{
 109        uintmax_t val;
 110        if (read_int(in, &val, len))
 111                return -1;
 112        if (val > SIZE_MAX)
 113                return error("unrepresentable length in delta: %"PRIuMAX"", val);
 114        *result = val;
 115        return 0;
 116}
 117
 118static int apply_one_window(struct line_buffer *delta, off_t *delta_len)
 119{
 120        struct window ctx = WINDOW_INIT;
 121        size_t out_len;
 122        size_t instructions_len;
 123        size_t data_len;
 124        assert(delta_len);
 125
 126        /* "source view" offset and length already handled; */
 127        if (read_length(delta, &out_len, delta_len) ||
 128            read_length(delta, &instructions_len, delta_len) ||
 129            read_length(delta, &data_len, delta_len) ||
 130            read_chunk(delta, delta_len, &ctx.instructions, instructions_len))
 131                goto error_out;
 132        if (instructions_len) {
 133                error("What do you think I am?  A delta applier?");
 134                goto error_out;
 135        }
 136        if (read_chunk(delta, delta_len, &ctx.data, data_len))
 137                goto error_out;
 138        window_release(&ctx);
 139        return 0;
 140error_out:
 141        window_release(&ctx);
 142        return -1;
 143}
 144
 145int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
 146                        struct sliding_view *preimage, FILE *postimage)
 147{
 148        assert(delta && preimage && postimage);
 149
 150        if (read_magic(delta, &delta_len))
 151                return -1;
 152        while (delta_len) {     /* For each window: */
 153                off_t pre_off;
 154                size_t pre_len;
 155
 156                if (read_offset(delta, &pre_off, &delta_len) ||
 157                    read_length(delta, &pre_len, &delta_len) ||
 158                    move_window(preimage, pre_off, pre_len) ||
 159                    apply_one_window(delta, &delta_len))
 160                        return -1;
 161        }
 162        return 0;
 163}