4fdd567a36a3f6b22a934838b46f6b2cd71fc3ec
   1/*
   2 * Copyright (c) 2011, Google Inc.
   3 */
   4#include "cache.h"
   5#include "streaming.h"
   6
   7enum input_source {
   8        stream_error = -1,
   9        incore = 0,
  10        loose = 1,
  11        pack_non_delta = 2
  12};
  13
  14typedef int (*open_istream_fn)(struct git_istream *,
  15                               struct object_info *,
  16                               const unsigned char *,
  17                               enum object_type *);
  18typedef int (*close_istream_fn)(struct git_istream *);
  19typedef ssize_t (*read_istream_fn)(struct git_istream *, char *, size_t);
  20
  21struct stream_vtbl {
  22        close_istream_fn close;
  23        read_istream_fn read;
  24};
  25
  26#define open_method_decl(name) \
  27        int open_istream_ ##name \
  28        (struct git_istream *st, struct object_info *oi, \
  29         const unsigned char *sha1, \
  30         enum object_type *type)
  31
  32#define close_method_decl(name) \
  33        int close_istream_ ##name \
  34        (struct git_istream *st)
  35
  36#define read_method_decl(name) \
  37        ssize_t read_istream_ ##name \
  38        (struct git_istream *st, char *buf, size_t sz)
  39
  40/* forward declaration */
  41static open_method_decl(incore);
  42static open_method_decl(loose);
  43static open_method_decl(pack_non_delta);
  44
  45static open_istream_fn open_istream_tbl[] = {
  46        open_istream_incore,
  47        open_istream_loose,
  48        open_istream_pack_non_delta,
  49};
  50
  51struct git_istream {
  52        const struct stream_vtbl *vtbl;
  53        unsigned long size; /* inflated size of full object */
  54        z_stream z;
  55        enum { z_unused, z_used, z_done, z_error } z_state;
  56
  57        union {
  58                struct {
  59                        char *buf; /* from read_object() */
  60                        unsigned long read_ptr;
  61                } incore;
  62
  63                struct {
  64                        int fd; /* open for reading */
  65                        /* NEEDSWORK: what else? */
  66                } loose;
  67
  68                struct {
  69                        struct packed_git *pack;
  70                        off_t pos;
  71                } in_pack;
  72        } u;
  73};
  74
  75int close_istream(struct git_istream *st)
  76{
  77        return st->vtbl->close(st);
  78}
  79
  80ssize_t read_istream(struct git_istream *st, char *buf, size_t sz)
  81{
  82        return st->vtbl->read(st, buf, sz);
  83}
  84
  85static enum input_source istream_source(const unsigned char *sha1,
  86                                        enum object_type *type,
  87                                        struct object_info *oi)
  88{
  89        unsigned long size;
  90        int status;
  91
  92        oi->sizep = &size;
  93        status = sha1_object_info_extended(sha1, oi);
  94        if (status < 0)
  95                return stream_error;
  96        *type = status;
  97
  98        switch (oi->whence) {
  99        case OI_LOOSE:
 100                return loose;
 101        case OI_PACKED:
 102                if (!oi->u.packed.is_delta && big_file_threshold <= size)
 103                        return pack_non_delta;
 104                /* fallthru */
 105        default:
 106                return incore;
 107        }
 108}
 109
 110struct git_istream *open_istream(const unsigned char *sha1,
 111                                 enum object_type *type,
 112                                 unsigned long *size)
 113{
 114        struct git_istream *st;
 115        struct object_info oi;
 116        const unsigned char *real = lookup_replace_object(sha1);
 117        enum input_source src = istream_source(real, type, &oi);
 118
 119        if (src < 0)
 120                return NULL;
 121
 122        st = xmalloc(sizeof(*st));
 123        if (open_istream_tbl[src](st, &oi, real, type)) {
 124                if (open_istream_incore(st, &oi, real, type)) {
 125                        free(st);
 126                        return NULL;
 127                }
 128        }
 129        *size = st->size;
 130        return st;
 131}
 132
 133
 134/*****************************************************************
 135 *
 136 * Common helpers
 137 *
 138 *****************************************************************/
 139
 140static void close_deflated_stream(struct git_istream *st)
 141{
 142        if (st->z_state == z_used)
 143                git_inflate_end(&st->z);
 144}
 145
 146
 147/*****************************************************************
 148 *
 149 * Loose object stream
 150 *
 151 *****************************************************************/
 152
 153static open_method_decl(loose)
 154{
 155        return -1; /* for now */
 156}
 157
 158
 159/*****************************************************************
 160 *
 161 * Non-delta packed object stream
 162 *
 163 *****************************************************************/
 164
 165static read_method_decl(pack_non_delta)
 166{
 167        size_t total_read = 0;
 168
 169        switch (st->z_state) {
 170        case z_unused:
 171                memset(&st->z, 0, sizeof(st->z));
 172                git_inflate_init(&st->z);
 173                st->z_state = z_used;
 174                break;
 175        case z_done:
 176                return 0;
 177        case z_error:
 178                return -1;
 179        case z_used:
 180                break;
 181        }
 182
 183        while (total_read < sz) {
 184                int status;
 185                struct pack_window *window = NULL;
 186                unsigned char *mapped;
 187
 188                mapped = use_pack(st->u.in_pack.pack, &window,
 189                                  st->u.in_pack.pos, &st->z.avail_in);
 190
 191                st->z.next_out = (unsigned char *)buf + total_read;
 192                st->z.avail_out = sz - total_read;
 193                st->z.next_in = mapped;
 194                status = git_inflate(&st->z, Z_FINISH);
 195
 196                st->u.in_pack.pos += st->z.next_in - mapped;
 197                total_read = st->z.next_out - (unsigned char *)buf;
 198                unuse_pack(&window);
 199
 200                if (status == Z_STREAM_END) {
 201                        git_inflate_end(&st->z);
 202                        st->z_state = z_done;
 203                        break;
 204                }
 205                if (status != Z_OK && status != Z_BUF_ERROR) {
 206                        git_inflate_end(&st->z);
 207                        st->z_state = z_error;
 208                        return -1;
 209                }
 210        }
 211        return total_read;
 212}
 213
 214static close_method_decl(pack_non_delta)
 215{
 216        close_deflated_stream(st);
 217        return 0;
 218}
 219
 220static struct stream_vtbl pack_non_delta_vtbl = {
 221        close_istream_pack_non_delta,
 222        read_istream_pack_non_delta,
 223};
 224
 225static open_method_decl(pack_non_delta)
 226{
 227        struct pack_window *window;
 228        enum object_type in_pack_type;
 229
 230        st->u.in_pack.pack = oi->u.packed.pack;
 231        st->u.in_pack.pos = oi->u.packed.offset;
 232        window = NULL;
 233
 234        in_pack_type = unpack_object_header(st->u.in_pack.pack,
 235                                            &window,
 236                                            &st->u.in_pack.pos,
 237                                            &st->size);
 238        unuse_pack(&window);
 239        switch (in_pack_type) {
 240        default:
 241                return -1; /* we do not do deltas for now */
 242        case OBJ_COMMIT:
 243        case OBJ_TREE:
 244        case OBJ_BLOB:
 245        case OBJ_TAG:
 246                break;
 247        }
 248        st->z_state = z_unused;
 249        st->vtbl = &pack_non_delta_vtbl;
 250        return 0;
 251}
 252
 253
 254/*****************************************************************
 255 *
 256 * In-core stream
 257 *
 258 *****************************************************************/
 259
 260static close_method_decl(incore)
 261{
 262        free(st->u.incore.buf);
 263        return 0;
 264}
 265
 266static read_method_decl(incore)
 267{
 268        size_t read_size = sz;
 269        size_t remainder = st->size - st->u.incore.read_ptr;
 270
 271        if (remainder <= read_size)
 272                read_size = remainder;
 273        if (read_size) {
 274                memcpy(buf, st->u.incore.buf + st->u.incore.read_ptr, read_size);
 275                st->u.incore.read_ptr += read_size;
 276        }
 277        return read_size;
 278}
 279
 280static struct stream_vtbl incore_vtbl = {
 281        close_istream_incore,
 282        read_istream_incore,
 283};
 284
 285static open_method_decl(incore)
 286{
 287        st->u.incore.buf = read_sha1_file_extended(sha1, type, &st->size, 0);
 288        st->u.incore.read_ptr = 0;
 289        st->vtbl = &incore_vtbl;
 290
 291        return st->u.incore.buf ? 0 : -1;
 292}