forked from fwk3d/v1
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ark.c
138 lines (123 loc) Β· 4.61 KB
/
ark.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
// ARK: lightweight, append-only, header-less journaling file format specification.
// - rlyeh, public domain.
// Features:
// - [x] Journaling support: data can be rolled back to an earlier state to retrieve older versions of files.
// - [x] Append-only format: create or update new entries just by appending stuff to the journal file.
// - [x] Compaction support: compact archives by keeping, for each duplicated file, its latest revision only.
// - [x] Concat friendly: journals can be glued together, and the result will still be a valid journey file.
// - [x] Foreign support: append random data to a foreign file and result will still be a valid journey file.
// - [x] Always aligned: file data is always 512-byte aligned for safe/fast memory access.
// - [x] Simple, tiny, portable, cross-platform, header-only.
// - [x] Public domain, CC0, 0-BSD, unlicensed (pick one).
// Extension:
// .ark
// File format:
// [foreign data]
// [...]
// [archive-entry #1]
// [archive-entry #2]
// [...]
// [archive-entry #N]
// [EOF]
//
// Where, each archive-entry is {
// [zero] 472-byte aligned zero padding
// [mark] 64-bit magic id 'ArK1' (if 1KrA is found, swap endianness)
// [time] 64-bit time stamp in seconds (unix epoch)
// [dlen] 64-bit data length
// [hash] 64-bit data hash
// [nlen] 64-bit name length+1
// [data] file data (512-byte aligned)
// [name] file name+NUL
// }
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define ARK1 'ArK1'
#define ARK1_PADDING (512 - 40) // 472
#ifndef ARK_PRINTF
#define ARK_PRINTF printf
#endif
#ifndef ARK_SWAP32
#define ARK_SWAP32(x) x
#define ARK_SWAP64(x) x
#endif
#ifndef ARK_REALLOC
#define ARK_REALLOC realloc
#endif
static int ark_fput64( FILE *out, uint64_t v ) { return fwrite( (v = ARK_SWAP64(v), &v), 1, 8, out); }
static uint64_t ark_fget64( FILE *in ) { uint64_t v; fread( &v, 1, 8, in ); return ARK_SWAP64(v); }
static const char ark1_zero[ARK1_PADDING] = {0};
// `outfile` must be fopen("file.ark", "a+b") beforehand
int ark_append_mem( FILE *out, const char *name, const void *data, int datalen, uint64_t stamp ) {
fseek( out, 0L, SEEK_END );
while( 0 != (ftell(out) % ARK1_PADDING) ) fwrite(ark1_zero, 1, ARK1_PADDING - (ftell(out) % ARK1_PADDING), out);
ARK_PRINTF("Writing %s at #%d\n", name, (int)ftell(out));
int namelen = strlen(name) + 1;
uint64_t mark = ARK1;
ark_fput64(out, mark);
ark_fput64(out, stamp);
ark_fput64(out, datalen);
ark_fput64(out, 0ULL/*hash*/);
ark_fput64(out, namelen);
fwrite(data, 1, datalen, out);
fwrite(name, 1, namelen, out);
return 1;
}
// `outfile` must be fopen("file.ark", "a+b") beforehand
int ark_append_file( FILE *out, const char *name ) {
FILE *in = fopen(name, "rb");
if( in ) {
fseek(in, 0L, SEEK_END);
size_t sz = ftell(in);
fseek(in, 0L, SEEK_SET);
char *buffer = ARK_REALLOC(0, sz);
if( !buffer ) return fclose(in), 0;
fread(buffer, 1, sz, in);
fclose(in);
int rc = ark_append_mem(out, name, buffer, sz, 0ULL);
ARK_REALLOC(buffer, 0);
return rc;
}
return 0;
}
void ark_list( FILE *in, void *yield_fn ) {
int (*ark_yield_fn)() = yield_fn;
while( !feof(in) ) {
while( 0 != (ftell(in) % ARK1_PADDING) && !feof(in) ) fseek(in, ARK1_PADDING - (ftell(in) % ARK1_PADDING), SEEK_CUR);
ARK_PRINTF("Reading at #%d\n", (int)ftell(in));
uint64_t mark = ark_fget64(in);
if( mark != ARK1 ) continue;
uint64_t stamp = ark_fget64(in);
uint64_t datalen = ark_fget64(in);
uint64_t datahash = ark_fget64(in);
uint64_t namelen = ark_fget64(in);
char *data = ARK_REALLOC(0, datalen);
fread(data, 1, datalen, in);
char *name = ARK_REALLOC(0, namelen);
fread(name, 1, namelen, in);
if( yield_fn == printf ) {
printf("Found %s (%d bytes)\n", name, (int)datalen );
ARK_REALLOC(name, 0);
ARK_REALLOC(data, 0);
}
else {
if( !ark_yield_fn(name, data, datalen, datahash, stamp) )
return;
}
}
}
int main(int argc, char **argv) {
if( argc > 2 ) {
FILE *ark = fopen(argv[1], "a+b");
if( ark ) for( int i = 2; i < argc; ++i) ark_append_file( ark, argv[i] );
if( ark ) fclose(ark);
}
else if( argc == 2 ) {
FILE *ark = fopen(argv[1], "rb");
if( ark ) ark_list(ark, printf), fclose(ark);
}
else printf("%s infile.ark\n%s outfile.ark infile1 [infile2...]\n", argv[0], argv[0]);
}
// cl ark.c /MT /O2 /DNDEBUG /link setargv.obj