/
vcdiff.c
278 lines (227 loc) · 7.21 KB
/
vcdiff.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/**
* MojoPatch; a tool for updating data in the field.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
/*
* (How is there no vcdiff implementation under the zlib license?!)
* Written based on the RFC: http://www.faqs.org/rfcs/rfc3284.html
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#if (defined(_MSC_VER) && !defined(inline))
#define inline _inline
#endif
static inline uint32 swapui32(uint32 x)
{
#if PLATFORM_BIGENDIAN
return (((x)>>24) + (((x)>>8)&0xff00) + (((x)<<8)&0xff0000) + ((x)<<24));
#else
return (x);
#endif
}
static void *internal_malloc(int bytes, void *d) { return malloc(bytes); }
static void internal_free(void *ptr, void *d) { free(ptr); }
static int64 stdio_read(void *ctx, void *buf, uint32 n)
{
FILE *io = (FILE *) ctx;
const int64 rc = (int64) fread(buf, 1, (size_t) n, io);
if ( (rc == 0) && (ferror(io)) )
return -1;
return rc;
} /* stdio_read */
static int64 stdio_write(void *ctx, void *buf, uint32 n)
{
FILE *io = (FILE *) ctx;
const int64 rc = (int64) fwrite(buf, 1, (size_t) n, io);
if ( (rc == 0) && (ferror(io)) )
return -1;
return rc;
} /* stdio_write */
static int64 stdio_seek(void *ctx, uint64 n)
{
FILE *io = (FILE *) ctx;
if (fseeko(io, (off_t) n, SEEK_SET) == -1)
return -1;
return (int64) ftello(io);
} /* stdio_seek */
/* More compact when you need: "this operation must not 'sort of' work". */
static inline int Read(vcdiff_io *io, void *buf, uint32 n)
{
return ( io->read(io->ctx, buf, n) == ((int64) n) );
} /* Read */
/* More compact when you need: "this operation must not 'sort of' work". */
static inline int Write(vcdiff_io *io, void *buf, uint32 n)
{
return ( io->write(io->ctx, buf, n) == ((int64) n) );
} /* Write */
/* More compact when you need: "this operation must not 'sort of' work". */
static inline int Seek(vcdiff_io *io, uint64 pos)
{
return ( io->seek(io->ctx, pos) == ((int64) pos) );
} /* Seek */
static int Read_ui32(vcdiff_io *io, uint32 *ui32)
{
if (!Read(io, ui32, sizeof (*ui32)))
return 0;
*ui32 = swapui32(*ui32);
return 1;
} /* Read_ui32 */
typedef struct
{
vcdiff_io *src;
vcdiff_io *delta;
vcdiff_io *dst;
uint8 compressor;
uint32 tablelen;
uint8 *codetable;
vcdiff_malloc malloc;
vcdiff_free free;
void *malloc_data;
} vcdiff_ctx;
/* Convenience functions for allocators... */
static inline void *Malloc(const vcdiff_ctx *ctx, const int len)
{
return ctx->malloc(len, ctx->malloc_data);
} /* Malloc */
static inline void Free(const Context *ctx, void *ptr)
{
if (ptr != NULL) /* check for NULL in case of dumb free() impl. */
ctx->free(ptr, ctx->malloc_data);
} /* Free */
static int read_delta_header(vcdiff_ctx *ctx)
{
vcdiff_io *io = ctx->delta;
uint8 sig[5];
if (!Read(io, sig, sizeof (sig)))
return 0;
/* magic signature. */
if ((sig[0]!=0xD6) || (sig[1]!=0xC3) || (sig[2]!=0xC4) || (sig[3]!=0x00))
return 0; /* not a delta file. */
else
{
const uint8 indicator = sig[4];
const int has_compressor = (indicator & (1 << 0)) ? 1 : 0;
const int has_codetable = (indicator & (1 << 1)) ? 1 : 0;
if ((indicator & 0xFC) != 0)
return 0; /* bits we weren't expecting are set. */
if (has_compressor)
{
if (!Read(io, &ctx->compressor, sizeof (compressor)))
return 0;
return 0; /* !!! FIXME: unsupported at the moment. */
} /* if */
if (has_codetable)
{
if (!Read_ui32(io, &ctx->tablelen))
return 0;
ctx->codetable = (uint8 *) Malloc(ctx, (size_t) ctx->tablelen);
if (ctx->codetable == NULL)
return 0;
if (!Read(io, ctx->codetable, ctx->tablelen))
return 0;
return 0; /* !!! FIXME: unsupported at the moment. */
} /* if */
} /* else */
return 1;
} /* read_delta_header */
static int _read_delta_window(vcdiff_ctx *ctx, const uint8 indicator)
{
vcdiff_io *io = ctx->delta;
const int source = (indicator & (1 << 0)) ? 1 : 0;
const int target = (indicator & (1 << 1)) ? 1 : 0;
uint32 len = 0;
uint32 pos = 0;
vcdiff_io *srcio = NULL;
uint8 *srcdata = NULL;
if ((indicator & 0xFC) != 0)
return 0; /* bits we weren't expecting are set. */
else if ((source) && (target))
return 0; /* can't have both! */
else if ((source) || (target))
{
if (!Read_ui32(io, &len))
return 0;
else if (!Read_ui32(io, &pos))
return 0;
srcio = (source) ? ctx->src : ctx->dst;
srcdata = (uint8 *) Malloc(ctx, len);
if (srcdata == NULL)
return 0;
if ( (!Seek(srcio, pos)) || (!Read(srcio, srcdata, len)) )
{
Free(src, srcdata);
return 0;
} /* if */
} /* else if */
Free(ctx, srcdata);
} /* _read_delta_window */
static int read_delta_window(vcdiff_ctx *ctx)
{
vcdiff_io *io = ctx->delta;
uint8 indicator;
int64 br = io->read(io->ctx, &indicator, sizeof (indicator));
if (br == 0)
return 0; /* EOF. We're done! */
else if (br == -1)
return -1; /* Error. We're also done. */
return _read_delta_window(ctx, indicator);
} /* read_delta_window */
/* so all logic uses ctx, instead of the mainline using &ctx ... */
static int _vcdiff(vcdiff_ctx *ctx)
{
if (!read_delta_header(ctx))
return 0;
else
{
int rc;
while ((rc = read_delta_window(ctx)) == 1) { /* keep looping. */ }
if (rc == -1)
return 0; /* error, not successful EOF. */
} /* else */
return 1; /* success. */
} /* _vcdiff */
int vcdiff(vcdiff_io *iosrc, vcdiff_io *iodelta, vcdiff_io *iodst,
vcdiff_malloc m, vcdiff_free f, void *d)
{
int retval = 0;
vcdiff_ctx ctx;
memset(&ctx, '\0', sizeof (ctx));
ctx.iosrc = iosrc;
ctx.iodelta = iodelta;
ctx.iodst = iodst;
ctx.malloc = (m != NULL) ? m : internal_malloc;
ctx.free = (f != NULL) ? f : internal_free;
ctx.malloc_data = d;
retval = _vcdiff(&ctx);
Free(&ctx, ctx.codetable);
return retval;
} /* vcdiff */
/* Please make sure all are seekable! */
int vcdiff_stdio(FILE *fiosrc, FILE *fiodelta, FILE *fiodst,
vcdiff_malloc m, vcdiff_free f, void *d)
{
vcdiff_io iosrc = { stdio_read, stdio_write, stdio_seek, fiosrc };
vcdiff_io iodelta = { stdio_read, stdio_write, stdio_seek, fiodelta };
vcdiff_io iodst = { stdio_read, stdio_write, stdio_seek, fiodst };
return vcdiff(&iosrc, &iodelta, &iodst, m, f, d);
} /* vcdiff_stdio */
/* All three of these are filenames. */
int vcdiff_fname(const char *src, const char *delta, const char *dst,
vcdiff_malloc m, vcdiff_free f, void *d)
{
FILE *iosrc = fopen(src, "rb");
FILE *iodelta = fopen(delta, "rb");
FILE *iodst = fopen(dst, "r+b");
const int rc = vcdiff_stdio(iosrc, iodelta, iodst, m, f, d);
fclose(iosrc);
fclose(iodelta);
fclose(iodst);
if (!rc)
unlink(dst);
return rc;
} /* vcdiff_fname */
/* end of vcdiff.c ... */