vcs-svn / svndiff.con commit vcs-svn: read inline data from deltas (fc4ae43)
   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 data;
  29};
  30
  31#define WINDOW_INIT     { STRBUF_INIT }
  32
  33static void window_release(struct window *ctx)
  34{
  35        strbuf_release(&ctx->data);
  36}
  37
  38static int error_short_read(struct line_buffer *input)
  39{
  40        if (buffer_ferror(input))
  41                return error("error reading delta: %s", strerror(errno));
  42        return error("invalid delta: unexpected end of file");
  43}
  44
  45static int read_chunk(struct line_buffer *delta, off_t *delta_len,
  46                      struct strbuf *buf, size_t len)
  47{
  48        strbuf_reset(buf);
  49        if (len > *delta_len ||
  50            buffer_read_binary(delta, buf, len) != len)
  51                return error_short_read(delta);
  52        *delta_len -= buf->len;
  53        return 0;
  54}
  55
  56static int read_magic(struct line_buffer *in, off_t *len)
  57{
  58        static const char magic[] = {'S', 'V', 'N', '\0'};
  59        struct strbuf sb = STRBUF_INIT;
  60
  61        if (read_chunk(in, len, &sb, sizeof(magic))) {
  62                strbuf_release(&sb);
  63                return -1;
  64        }
  65        if (memcmp(sb.buf, magic, sizeof(magic))) {
  66                strbuf_release(&sb);
  67                return error("invalid delta: unrecognized file type");
  68        }
  69        strbuf_release(&sb);
  70        return 0;
  71}
  72
  73static int read_int(struct line_buffer *in, uintmax_t *result, off_t *len)
  74{
  75        uintmax_t rv = 0;
  76        off_t sz;
  77        for (sz = *len; sz; sz--) {
  78                const int ch = buffer_read_char(in);
  79                if (ch == EOF)
  80                        break;
  81
  82                rv <<= VLI_BITS_PER_DIGIT;
  83                rv += (ch & VLI_DIGIT_MASK);
  84                if (ch & VLI_CONTINUE)
  85                        continue;
  86
  87                *result = rv;
  88                *len = sz - 1;
  89                return 0;
  90        }
  91        return error_short_read(in);
  92}
  93
  94static int read_offset(struct line_buffer *in, off_t *result, off_t *len)
  95{
  96        uintmax_t val;
  97        if (read_int(in, &val, len))
  98                return -1;
  99        if (val > maximum_signed_value_of_type(off_t))
 100                return error("unrepresentable offset in delta: %"PRIuMAX"", val);
 101        *result = val;
 102        return 0;
 103}
 104
 105static int read_length(struct line_buffer *in, size_t *result, off_t *len)
 106{
 107        uintmax_t val;
 108        if (read_int(in, &val, len))
 109                return -1;
 110        if (val > SIZE_MAX)
 111                return error("unrepresentable length in delta: %"PRIuMAX"", val);
 112        *result = val;
 113        return 0;
 114}
 115
 116static int apply_one_window(struct line_buffer *delta, off_t *delta_len)
 117{
 118        struct window ctx = WINDOW_INIT;
 119        size_t out_len;
 120        size_t instructions_len;
 121        size_t data_len;
 122        assert(delta_len);
 123
 124        /* "source view" offset and length already handled; */
 125        if (read_length(delta, &out_len, delta_len) ||
 126            read_length(delta, &instructions_len, delta_len) ||
 127            read_length(delta, &data_len, delta_len))
 128                goto error_out;
 129        if (instructions_len) {
 130                error("What do you think I am?  A delta applier?");
 131                goto error_out;
 132        }
 133        if (read_chunk(delta, delta_len, &ctx.data, data_len))
 134                goto error_out;
 135        window_release(&ctx);
 136        return 0;
 137error_out:
 138        window_release(&ctx);
 139        return -1;
 140}
 141
 142int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
 143                        struct sliding_view *preimage, FILE *postimage)
 144{
 145        assert(delta && preimage && postimage);
 146
 147        if (read_magic(delta, &delta_len))
 148                return -1;
 149        while (delta_len) {     /* For each window: */
 150                off_t pre_off;
 151                size_t pre_len;
 152
 153                if (read_offset(delta, &pre_off, &delta_len) ||
 154                    read_length(delta, &pre_len, &delta_len) ||
 155                    move_window(preimage, pre_off, pre_len) ||
 156                    apply_one_window(delta, &delta_len))
 157                        return -1;
 158        }
 159        return 0;
 160}