1 /* Distributed under the 4-part Berkeley License */
2
3 /*
4 * afflib_dir.cpp:
5 *
6 * Functions for the manipulation of the AFF directories
7 */
8
9 #include "affconfig.h"
10 #include "afflib.h"
11 #include "afflib_i.h"
12
aff_toc_free(AFFILE * af)13 int aff_toc_free(AFFILE *af)
14 {
15 if(af->toc){
16 for(int i=0;i<af->toc_count;i++){
17 if(af->toc[i].name) free(af->toc[i].name);
18 }
19 free(af->toc);
20 af->toc = 0;
21 af->toc_count = 0;
22 }
23 return 0;
24 }
25
26
aff_toc_print(AFFILE * af)27 void aff_toc_print(AFFILE *af)
28 {
29 printf("AF DIRECTORY:\n");
30 for(int i=0;i<af->toc_count;i++){
31 if(af->toc[i].name){
32 printf("%-32s @%" I64u " len: %" I64u " \n",af->toc[i].name, af->toc[i].offset,af->toc[i].segment_len);
33 }
34 }
35 }
36
37 #if 0
38 static int toc_sort(const void *a_,const void *b_)
39 {
40 const aff_toc_mem *a = (const aff_toc_mem *)a_;
41 const aff_toc_mem *b = (const aff_toc_mem *)b_;
42 if(a->offset < b->offset) return -1;
43 if(a->offset > b->offset) return 1;
44 return 0;
45 }
46 #endif
47
aff_toc_append(AFFILE * af,const char * segname,uint64_t offset,uint64_t datalen)48 static int aff_toc_append(AFFILE *af,const char *segname,uint64_t offset,uint64_t datalen)
49 {
50 /* don't add AF_IGNORE segments to TOC */
51 if(!segname[0])
52 return 0;
53
54 af->toc = (aff_toc_mem *)realloc(af->toc,sizeof(*af->toc)*(af->toc_count+1));
55 if(af->toc==0){
56 (*af->error_reporter)("realloc() failed in aff_toc_append. toc_count=%d\n",af->toc_count);
57 return -1;
58 }
59 af->toc[af->toc_count].offset = offset;
60 af->toc[af->toc_count].name = strdup(segname); // make a copy of the string
61 af->toc[af->toc_count].segment_len = aff_segment_overhead(segname)+datalen;
62 af->toc_count++;
63 return 0;
64 }
65
66 /* Find an empty slot in the TOC in which to put the TOC.
67 * Otherwise add it to the end.
68 */
aff_toc_update(AFFILE * af,const char * segname,uint64_t offset,uint64_t datalen)69 void aff_toc_update(AFFILE *af,const char *segname,uint64_t offset,uint64_t datalen)
70 {
71 /* don't add AF_IGNORE segments to TOC */
72 if(!segname[0])
73 return;
74
75 for(int i=0;i<af->toc_count;i++){
76 if(af->toc[i].name==0 || strcmp(af->toc[i].name,segname)==0){
77 if(af->toc[i].name==0){ // if name was empty, copy it over
78 af->toc[i].name = strdup(segname);
79 }
80 af->toc[i].offset = offset;
81 af->toc[i].segment_len = aff_segment_overhead(segname)+datalen;
82 return;
83 }
84 }
85 aff_toc_append(af,segname,offset,datalen); /* Need to append it to the directory */
86 }
87
88 /* exact compression settings are not saved in the AFF file, so make a best guess */
guess_compression(AFFILE * af,const char * segname,uint32_t flags)89 static void guess_compression(AFFILE *af, const char *segname, uint32_t flags)
90 {
91 /* ignore non-data segments */
92 if(af_segname_page_number(segname) < 0)
93 return;
94
95 if(flags & AF_PAGE_COMPRESSED)
96 {
97 switch(flags & AF_PAGE_COMP_ALG_MASK)
98 {
99 case AF_PAGE_COMP_ALG_ZLIB:
100 af->compression_type = AF_COMPRESSION_ALG_ZLIB;
101 af->compression_level = (flags & AF_PAGE_COMP_MAX) ? AF_COMPRESSION_MAX : AF_COMPRESSION_DEFAULT;
102 break;
103
104 case AF_PAGE_COMP_ALG_LZMA:
105 af->compression_type = AF_COMPRESSION_ALG_LZMA;
106 af->compression_level = (flags & AF_PAGE_COMP_MAX) ? AF_COMPRESSION_MAX : AF_COMPRESSION_DEFAULT;
107 break;
108 }
109 }
110 else
111 {
112 af->compression_type = AF_COMPRESSION_ALG_NONE;
113 af->compression_level = 0;
114 }
115 }
116
117 /*
118 * aff_toc_build:
119 * Build the directory by reading the existing file.
120 * Notice that we know that we can simply append.
121 */
aff_toc_build(AFFILE * af)122 int aff_toc_build(AFFILE *af) // build the dir if we couldn't find it
123 {
124 aff_toc_free(af); // clear the old one
125 af_rewind_seg(af); // start at the beginning
126
127 /* default compression settings */
128 af->compression_type = AF_COMPRESSION_ALG_ZLIB;
129 af->compression_level = AF_COMPRESSION_DEFAULT;
130
131 // note: was malloc(0), but that causes problems under Borland
132 af->toc = (aff_toc_mem *)malloc(sizeof(aff_toc_mem));
133 while(1){
134 char segname[AF_MAX_NAME_LEN];
135 size_t segname_len=sizeof(segname);
136 uint64_t pos = ftello(af->aseg);
137 uint32_t flags = 0;
138 size_t datalen=0;
139
140 errno = 0;
141 int r = af_get_next_seg(af, segname, segname_len, &flags, 0, &datalen);
142 switch(r){
143 case AF_ERROR_NO_ERROR:
144 guess_compression(af, segname, flags);
145 if(aff_toc_append(af,segname,pos,datalen)){
146 return -1; // malloc error?
147 }
148 break;
149 case AF_ERROR_EOF:
150 return 0; // end of file; no errors
151 default: /* unknown error */
152 if(!errno)
153 errno = EIO;
154 return r; // send up the error code
155 }
156 }
157 return AF_ERROR_NO_ERROR;
158 }
159
160 /*
161 * return the named entry in the directory
162 */
163
aff_toc(AFFILE * af,const char * segname)164 struct aff_toc_mem *aff_toc(AFFILE *af,const char *segname)
165 {
166 for(int i=0;i<af->toc_count;i++){
167 if(af->toc[i].name && strcmp(af->toc[i].name,segname)==0) return &af->toc[i];
168 }
169 return 0;
170 }
171
172 /* Delete something from the directory, but don't bother reallocating.*/
aff_toc_del(AFFILE * af,const char * segname)173 int aff_toc_del(AFFILE *af,const char *segname)
174 {
175 for(int i=0;i<af->toc_count;i++){
176 if(af->toc[i].name && strcmp(af->toc[i].name,segname)==0){
177 free(af->toc[i].name);
178 af->toc[i].name=0;
179 return 0; // should only be in TOC once
180 }
181 }
182 return -1;
183 }
184
185 /* Finds the next segment starting at or beyond a given byte offset. */
186 /* Returns the TOC entry if found; null if not found. */
aff_toc_next_seg(AFFILE * af,uint64_t offset)187 struct aff_toc_mem *aff_toc_next_seg(AFFILE *af, uint64_t offset)
188 {
189 struct aff_toc_mem *next_seg = 0;
190 struct aff_toc_mem *end = af->toc + af->toc_count;
191
192 for(struct aff_toc_mem *seg = af->toc; seg != end; seg++)
193 {
194 if(!seg->name)
195 continue;
196
197 if(seg->offset >= offset && (!next_seg || seg->offset < next_seg->offset))
198 next_seg = seg;
199 }
200
201 return next_seg;
202 }
203
204 /* Finds the smallest unused block of at least min_size bytes. */
205 /* Returns 0 if found, -1 if not found. */
aff_toc_find_hole(AFFILE * af,uint64_t min_size,uint64_t * offset,uint64_t * size)206 int aff_toc_find_hole(AFFILE *af, uint64_t min_size, uint64_t *offset, uint64_t *size)
207 {
208 int ret = -1;
209 uint64_t search_offset = 0;
210 struct aff_toc_mem *seg;
211
212 while((seg = aff_toc_next_seg(af, search_offset)))
213 {
214 uint64_t test_size = seg->offset - search_offset;
215
216 if(test_size >= min_size && (ret < 0 || test_size < *size))
217 {
218 *offset = search_offset;
219 *size = test_size;
220 ret = 0;
221 }
222
223 search_offset = seg->offset + seg->segment_len;
224 }
225
226 return ret;
227 }
228