Fossil SCM

Merge the small-stack changes into the trunk. This completes the fix for ticket [2a1e8e3c4b0b39e08fdde]

drh 2010-10-05 03:29 trunk merge
Commit b8f134bbbb23f68c9b7db553f836d60d5fe0291c
4 files changed +160 -142 +160 -142 +72 -66 +4 -2
+160 -142
--- src/content.c
+++ src/content.c
@@ -19,33 +19,25 @@
1919
*/
2020
#include "config.h"
2121
#include "content.h"
2222
#include <assert.h>
2323
24
-/*
25
-** Macros for debugging
26
-*/
27
-#if 0
28
-# define CONTENT_TRACE(X) printf X;
29
-#else
30
-# define CONTENT_TRACE(X)
31
-#endif
32
-
3324
/*
3425
** The artifact retrival cache
3526
*/
36
-#define MX_CACHE_CNT 50 /* Maximum number of positive cache entries */
37
-#define EXPELL_INTERVAL 5 /* How often to expell from a full cache */
3827
static struct {
39
- int n; /* Current number of positive cache entries */
28
+ i64 szTotal; /* Total size of all entries in the cache */
29
+ int n; /* Current number of eache entries */
30
+ int nAlloc; /* Number of slots allocated in a[] */
4031
int nextAge; /* Age counter for implementing LRU */
4132
int skipCnt; /* Used to limit entries expelled from cache */
42
- struct { /* One instance of this for each cache entry */
33
+ struct cacheLine { /* One instance of this for each cache entry */
4334
int rid; /* Artifact id */
4435
int age; /* Age. Newer is larger */
4536
Blob content; /* Content of the artifact */
46
- } a[MX_CACHE_CNT]; /* The positive cache */
37
+ } *a; /* The positive cache */
38
+ Bag inCache; /* Set of artifacts currently in cache */
4739
4840
/*
4941
** The missing artifact cache.
5042
**
5143
** Artifacts whose record ID are in missingCache cannot be retrieved
@@ -56,10 +48,54 @@
5648
*/
5749
Bag missing; /* Cache of artifacts that are incomplete */
5850
Bag available; /* Cache of artifacts that are complete */
5951
} contentCache;
6052
53
+/*
54
+** Remove the oldest element from the content cache
55
+*/
56
+static void content_cache_expire_oldest(void){
57
+ int i;
58
+ int mnAge = contentCache.nextAge;
59
+ int mn = -1;
60
+ for(i=0; i<contentCache.n; i++){
61
+ if( contentCache.a[i].age<mnAge ){
62
+ mnAge = contentCache.a[i].age;
63
+ mn = i;
64
+ }
65
+ }
66
+ if( mn>=0 ){
67
+ bag_remove(&contentCache.inCache, contentCache.a[mn].rid);
68
+ contentCache.szTotal -= blob_size(&contentCache.a[mn].content);
69
+ blob_reset(&contentCache.a[mn].content);
70
+ contentCache.n--;
71
+ contentCache.a[mn] = contentCache.a[contentCache.n];
72
+ }
73
+}
74
+
75
+/*
76
+** Add an entry to the content cache
77
+*/
78
+void content_cache_insert(int rid, Blob *pBlob){
79
+ struct cacheLine *p;
80
+ if( contentCache.n>500 || contentCache.szTotal>50000000 ){
81
+ content_cache_expire_oldest();
82
+ }
83
+ if( contentCache.n>=contentCache.nAlloc ){
84
+ contentCache.nAlloc = contentCache.nAlloc*2 + 10;
85
+ contentCache.a = realloc(contentCache.a,
86
+ contentCache.nAlloc*sizeof(contentCache.a[0]));
87
+ if( contentCache.a==0 ) fossil_panic("out of memory");
88
+ }
89
+ p = &contentCache.a[contentCache.n++];
90
+ p->rid = rid;
91
+ p->age = contentCache.nextAge++;
92
+ contentCache.szTotal += blob_size(pBlob);
93
+ p->content = *pBlob;
94
+ blob_zero(pBlob);
95
+ bag_insert(&contentCache.inCache, rid);
96
+}
6197
6298
/*
6399
** Clear the content cache.
64100
*/
65101
void content_clear_cache(void){
@@ -67,11 +103,13 @@
67103
for(i=0; i<contentCache.n; i++){
68104
blob_reset(&contentCache.a[i].content);
69105
}
70106
bag_clear(&contentCache.missing);
71107
bag_clear(&contentCache.available);
108
+ bag_clear(&contentCache.inCache);
72109
contentCache.n = 0;
110
+ contentCache.szTotal = 0;
73111
}
74112
75113
/*
76114
** Return the srcid associated with rid. Or return 0 if rid is
77115
** original content and not a delta.
@@ -95,32 +133,31 @@
95133
** true if it is. Return false if rid is a phantom or depends on
96134
** a phantom.
97135
*/
98136
int content_is_available(int rid){
99137
int srcid;
100
- if( bag_find(&contentCache.missing, rid) ){
101
- return 0;
102
- }
103
- if( bag_find(&contentCache.available, rid) ){
104
- return 1;
105
- }
106
- if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
107
- bag_insert(&contentCache.missing, rid);
108
- return 0;
109
- }
110
- srcid = findSrcid(rid);
111
- if( srcid==0 ){
112
- bag_insert(&contentCache.available, rid);
113
- return 1;
114
- }
115
- if( content_is_available(srcid) ){
116
- bag_insert(&contentCache.available, rid);
117
- return 1;
118
- }else{
119
- bag_insert(&contentCache.missing, rid);
120
- return 0;
121
- }
138
+ int depth = 0; /* Limit to recursion depth */
139
+ while( depth++ < 10000000 ){
140
+ if( bag_find(&contentCache.missing, rid) ){
141
+ return 0;
142
+ }
143
+ if( bag_find(&contentCache.available, rid) ){
144
+ return 1;
145
+ }
146
+ if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
147
+ bag_insert(&contentCache.missing, rid);
148
+ return 0;
149
+ }
150
+ srcid = findSrcid(rid);
151
+ if( srcid==0 ){
152
+ bag_insert(&contentCache.available, rid);
153
+ return 1;
154
+ }
155
+ rid = srcid;
156
+ }
157
+ fossil_panic("delta-loop in repository");
158
+ return 0;
122159
}
123160
124161
/*
125162
** Mark artifact rid as being available now. Update the cache to
126163
** show that everything that was formerly unavailable because rid
@@ -163,113 +200,84 @@
163200
}
164201
db_reset(&q);
165202
return rc;
166203
}
167204
168
-
169205
/*
170206
** Extract the content for ID rid and put it into the
171207
** uninitialized blob. Return 1 on success. If the record
172208
** is a phantom, zero pBlob and return 0.
173209
*/
174210
int content_get(int rid, Blob *pBlob){
175
- Blob src;
176
- int srcid;
177
- int rc = 0;
211
+ int rc;
178212
int i;
179
- static Bag inProcess;
213
+ int nextRid;
180214
181215
assert( g.repositoryOpen );
182216
blob_zero(pBlob);
183217
if( rid==0 ) return 0;
184218
185219
/* Early out if we know the content is not available */
186220
if( bag_find(&contentCache.missing, rid) ){
187
- CONTENT_TRACE(("%*smiss from cache: %d\n",
188
- bag_count(&inProcess), "", rid))
189221
return 0;
190222
}
191223
192224
/* Look for the artifact in the cache first */
193
- for(i=0; i<contentCache.n; i++){
194
- if( contentCache.a[i].rid==rid ){
195
- *pBlob = contentCache.a[i].content;
196
- blob_zero(&contentCache.a[i].content);
197
- contentCache.n--;
198
- if( i<contentCache.n ){
199
- contentCache.a[i] = contentCache.a[contentCache.n];
200
- }
201
- CONTENT_TRACE(("%*scache: %d\n",
202
- bag_count(&inProcess), "", rid))
203
- return 1;
204
- }
205
- }
206
-
207
- /* See if we need to apply a delta to find this artifact */
208
- srcid = findSrcid(rid);
209
- CONTENT_TRACE(("%*ssearching for %d. Need %d.\n",
210
- bag_count(&inProcess), "", rid, srcid))
211
-
212
-
213
- if( srcid ){
214
- /* Yes, a delta is required */
215
- if( bag_find(&inProcess, srcid) ){
216
- db_multi_exec(
217
- "UPDATE blob SET content=NULL, size=-1 WHERE rid=%d;"
218
- "DELETE FROM delta WHERE rid=%d;"
219
- "INSERT OR IGNORE INTO phantom VALUES(%d);",
220
- srcid, srcid, srcid
221
- );
222
- blob_zero(pBlob);
223
- return 0;
224
- }
225
- bag_insert(&inProcess, srcid);
226
-
227
- if( content_get(srcid, &src) ){
228
- Blob delta;
229
- if( content_of_blob(rid, &delta) ){
230
- blob_init(pBlob,0,0);
231
- blob_delta_apply(&src, &delta, pBlob);
225
+ if( bag_find(&contentCache.inCache, rid) ){
226
+ for(i=0; i<contentCache.n; i++){
227
+ if( contentCache.a[i].rid==rid ){
228
+ blob_copy(pBlob, &contentCache.a[i].content);
229
+ contentCache.a[i].age = contentCache.nextAge++;
230
+ return 1;
231
+ }
232
+ }
233
+ }
234
+
235
+ nextRid = findSrcid(rid);
236
+ if( nextRid==0 ){
237
+ rc = content_of_blob(rid, pBlob);
238
+ }else{
239
+ int n = 1;
240
+ int nAlloc = 10;
241
+ int *a = 0;
242
+ int mx;
243
+ Blob delta, next;
244
+
245
+ a = malloc( sizeof(a[0])*nAlloc );
246
+ if( a==0 ) fossil_panic("out of memory");
247
+ a[0] = rid;
248
+ a[1] = nextRid;
249
+ n = 1;
250
+ while( !bag_find(&contentCache.inCache, nextRid)
251
+ && (nextRid = findSrcid(nextRid))>0 ){
252
+ n++;
253
+ if( n>=nAlloc ){
254
+ nAlloc = nAlloc*2 + 10;
255
+ a = realloc(a, nAlloc*sizeof(a[0]));
256
+ if( a==0 ) fossil_panic("out of memory");
257
+ }
258
+ a[n] = nextRid;
259
+ }
260
+ mx = n;
261
+ rc = content_get(a[n], pBlob);
262
+ n--;
263
+ while( rc && n>=0 ){
264
+ rc = content_of_blob(a[n], &delta);
265
+ if( rc ){
266
+ blob_delta_apply(pBlob, &delta, &next);
232267
blob_reset(&delta);
233
- rc = 1;
234
- }
235
-
236
- /* Save the srcid artifact in the cache */
237
- if( contentCache.n<MX_CACHE_CNT ){
238
- i = contentCache.n++;
239
- }else if( ((contentCache.skipCnt++)%EXPELL_INTERVAL)!=0 ){
240
- i = -1;
241
- }else{
242
- int j, best;
243
- best = contentCache.nextAge+1;
244
- i = -1;
245
- for(j=0; j<contentCache.n; j++){
246
- if( contentCache.a[j].age<best ){
247
- i = j;
248
- best = contentCache.a[j].age;
249
- }
250
- }
251
- CONTENT_TRACE(("%*sexpell %d from cache\n",
252
- bag_count(&inProcess), "", contentCache.a[i].rid))
253
- blob_reset(&contentCache.a[i].content);
254
- }
255
- if( i>=0 ){
256
- contentCache.a[i].content = src;
257
- contentCache.a[i].age = contentCache.nextAge++;
258
- contentCache.a[i].rid = srcid;
259
- CONTENT_TRACE(("%*sadd %d to cache\n",
260
- bag_count(&inProcess), "", srcid))
261
- }else{
262
- blob_reset(&src);
263
- }
264
- }
265
- bag_remove(&inProcess, srcid);
266
- }else{
267
- /* No delta required. Read content directly from the database */
268
- if( content_of_blob(rid, pBlob) ){
269
- rc = 1;
270
- }
268
+ if( (mx-n)%8==0 ){
269
+ content_cache_insert(a[n+1], pBlob);
270
+ }else{
271
+ blob_reset(pBlob);
272
+ }
273
+ *pBlob = next;
274
+ }
275
+ n--;
276
+ }
277
+ free(a);
278
+ if( !rc ) blob_reset(pBlob);
271279
}
272280
if( rc==0 ){
273281
bag_insert(&contentCache.missing, rid);
274282
}else{
275283
bag_insert(&contentCache.available, rid);
@@ -320,35 +328,45 @@
320328
321329
/*
322330
** When a record is converted from a phantom to a real record,
323331
** if that record has other records that are derived by delta,
324332
** then call manifest_crosslink() on those other records.
333
+**
334
+** Tail recursion is used to minimize stack depth.
325335
*/
326336
void after_dephantomize(int rid, int linkFlag){
327337
Stmt q;
328
- int prevTid = 0;
329
-
330
- /* The prevTid variable is used to delay invoking this routine
331
- ** recursively, if possible, until after the query has finalized,
332
- ** in order to avoid having an excessive number of prepared statements.
333
- ** This is most effective in the common case where the query returns
334
- ** just one row.
335
- */
336
- db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
337
- while( db_step(&q)==SQLITE_ROW ){
338
- int tid = db_column_int(&q, 0);
339
- if( prevTid ) after_dephantomize(prevTid, 1);
340
- prevTid = tid;
341
- }
342
- db_finalize(&q);
343
- if( prevTid ) after_dephantomize(prevTid, 1);
344
- if( linkFlag ){
345
- Blob content;
346
- content_get(rid, &content);
347
- manifest_crosslink(rid, &content);
348
- blob_reset(&content);
349
- }
338
+ int nChildAlloc = 0;
339
+ int *aChild = 0;
340
+
341
+ while( rid ){
342
+ int nChildUsed = 0;
343
+ int i;
344
+ if( linkFlag ){
345
+ Blob content;
346
+ content_get(rid, &content);
347
+ manifest_crosslink(rid, &content);
348
+ blob_reset(&content);
349
+ }
350
+ db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
351
+ while( db_step(&q)==SQLITE_ROW ){
352
+ int child = db_column_int(&q, 0);
353
+ if( nChildUsed>=nChildAlloc ){
354
+ nChildAlloc = nChildAlloc*2 + 10;
355
+ aChild = realloc(aChild, nChildAlloc*sizeof(aChild));
356
+ if( aChild==0 ) fossil_panic("out of memory");
357
+ }
358
+ aChild[nChildUsed++] = child;
359
+ }
360
+ db_finalize(&q);
361
+ for(i=1; i<nChildUsed; i++){
362
+ after_dephantomize(aChild[i], 1);
363
+ }
364
+ rid = nChildUsed>0 ? aChild[0] : 0;
365
+ linkFlag = 1;
366
+ }
367
+ free(aChild);
350368
}
351369
352370
/*
353371
** Write content into the database. Return the record ID. If the
354372
** content is already in the database, just return the record ID.
355373
--- src/content.c
+++ src/content.c
@@ -19,33 +19,25 @@
19 */
20 #include "config.h"
21 #include "content.h"
22 #include <assert.h>
23
24 /*
25 ** Macros for debugging
26 */
27 #if 0
28 # define CONTENT_TRACE(X) printf X;
29 #else
30 # define CONTENT_TRACE(X)
31 #endif
32
33 /*
34 ** The artifact retrival cache
35 */
36 #define MX_CACHE_CNT 50 /* Maximum number of positive cache entries */
37 #define EXPELL_INTERVAL 5 /* How often to expell from a full cache */
38 static struct {
39 int n; /* Current number of positive cache entries */
 
 
40 int nextAge; /* Age counter for implementing LRU */
41 int skipCnt; /* Used to limit entries expelled from cache */
42 struct { /* One instance of this for each cache entry */
43 int rid; /* Artifact id */
44 int age; /* Age. Newer is larger */
45 Blob content; /* Content of the artifact */
46 } a[MX_CACHE_CNT]; /* The positive cache */
 
47
48 /*
49 ** The missing artifact cache.
50 **
51 ** Artifacts whose record ID are in missingCache cannot be retrieved
@@ -56,10 +48,54 @@
56 */
57 Bag missing; /* Cache of artifacts that are incomplete */
58 Bag available; /* Cache of artifacts that are complete */
59 } contentCache;
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
62 /*
63 ** Clear the content cache.
64 */
65 void content_clear_cache(void){
@@ -67,11 +103,13 @@
67 for(i=0; i<contentCache.n; i++){
68 blob_reset(&contentCache.a[i].content);
69 }
70 bag_clear(&contentCache.missing);
71 bag_clear(&contentCache.available);
 
72 contentCache.n = 0;
 
73 }
74
75 /*
76 ** Return the srcid associated with rid. Or return 0 if rid is
77 ** original content and not a delta.
@@ -95,32 +133,31 @@
95 ** true if it is. Return false if rid is a phantom or depends on
96 ** a phantom.
97 */
98 int content_is_available(int rid){
99 int srcid;
100 if( bag_find(&contentCache.missing, rid) ){
101 return 0;
102 }
103 if( bag_find(&contentCache.available, rid) ){
104 return 1;
105 }
106 if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
107 bag_insert(&contentCache.missing, rid);
108 return 0;
109 }
110 srcid = findSrcid(rid);
111 if( srcid==0 ){
112 bag_insert(&contentCache.available, rid);
113 return 1;
114 }
115 if( content_is_available(srcid) ){
116 bag_insert(&contentCache.available, rid);
117 return 1;
118 }else{
119 bag_insert(&contentCache.missing, rid);
120 return 0;
121 }
122 }
123
124 /*
125 ** Mark artifact rid as being available now. Update the cache to
126 ** show that everything that was formerly unavailable because rid
@@ -163,113 +200,84 @@
163 }
164 db_reset(&q);
165 return rc;
166 }
167
168
169 /*
170 ** Extract the content for ID rid and put it into the
171 ** uninitialized blob. Return 1 on success. If the record
172 ** is a phantom, zero pBlob and return 0.
173 */
174 int content_get(int rid, Blob *pBlob){
175 Blob src;
176 int srcid;
177 int rc = 0;
178 int i;
179 static Bag inProcess;
180
181 assert( g.repositoryOpen );
182 blob_zero(pBlob);
183 if( rid==0 ) return 0;
184
185 /* Early out if we know the content is not available */
186 if( bag_find(&contentCache.missing, rid) ){
187 CONTENT_TRACE(("%*smiss from cache: %d\n",
188 bag_count(&inProcess), "", rid))
189 return 0;
190 }
191
192 /* Look for the artifact in the cache first */
193 for(i=0; i<contentCache.n; i++){
194 if( contentCache.a[i].rid==rid ){
195 *pBlob = contentCache.a[i].content;
196 blob_zero(&contentCache.a[i].content);
197 contentCache.n--;
198 if( i<contentCache.n ){
199 contentCache.a[i] = contentCache.a[contentCache.n];
200 }
201 CONTENT_TRACE(("%*scache: %d\n",
202 bag_count(&inProcess), "", rid))
203 return 1;
204 }
205 }
206
207 /* See if we need to apply a delta to find this artifact */
208 srcid = findSrcid(rid);
209 CONTENT_TRACE(("%*ssearching for %d. Need %d.\n",
210 bag_count(&inProcess), "", rid, srcid))
211
212
213 if( srcid ){
214 /* Yes, a delta is required */
215 if( bag_find(&inProcess, srcid) ){
216 db_multi_exec(
217 "UPDATE blob SET content=NULL, size=-1 WHERE rid=%d;"
218 "DELETE FROM delta WHERE rid=%d;"
219 "INSERT OR IGNORE INTO phantom VALUES(%d);",
220 srcid, srcid, srcid
221 );
222 blob_zero(pBlob);
223 return 0;
224 }
225 bag_insert(&inProcess, srcid);
226
227 if( content_get(srcid, &src) ){
228 Blob delta;
229 if( content_of_blob(rid, &delta) ){
230 blob_init(pBlob,0,0);
231 blob_delta_apply(&src, &delta, pBlob);
 
 
 
232 blob_reset(&delta);
233 rc = 1;
234 }
235
236 /* Save the srcid artifact in the cache */
237 if( contentCache.n<MX_CACHE_CNT ){
238 i = contentCache.n++;
239 }else if( ((contentCache.skipCnt++)%EXPELL_INTERVAL)!=0 ){
240 i = -1;
241 }else{
242 int j, best;
243 best = contentCache.nextAge+1;
244 i = -1;
245 for(j=0; j<contentCache.n; j++){
246 if( contentCache.a[j].age<best ){
247 i = j;
248 best = contentCache.a[j].age;
249 }
250 }
251 CONTENT_TRACE(("%*sexpell %d from cache\n",
252 bag_count(&inProcess), "", contentCache.a[i].rid))
253 blob_reset(&contentCache.a[i].content);
254 }
255 if( i>=0 ){
256 contentCache.a[i].content = src;
257 contentCache.a[i].age = contentCache.nextAge++;
258 contentCache.a[i].rid = srcid;
259 CONTENT_TRACE(("%*sadd %d to cache\n",
260 bag_count(&inProcess), "", srcid))
261 }else{
262 blob_reset(&src);
263 }
264 }
265 bag_remove(&inProcess, srcid);
266 }else{
267 /* No delta required. Read content directly from the database */
268 if( content_of_blob(rid, pBlob) ){
269 rc = 1;
270 }
271 }
272 if( rc==0 ){
273 bag_insert(&contentCache.missing, rid);
274 }else{
275 bag_insert(&contentCache.available, rid);
@@ -320,35 +328,45 @@
320
321 /*
322 ** When a record is converted from a phantom to a real record,
323 ** if that record has other records that are derived by delta,
324 ** then call manifest_crosslink() on those other records.
 
 
325 */
326 void after_dephantomize(int rid, int linkFlag){
327 Stmt q;
328 int prevTid = 0;
329
330 /* The prevTid variable is used to delay invoking this routine
331 ** recursively, if possible, until after the query has finalized,
332 ** in order to avoid having an excessive number of prepared statements.
333 ** This is most effective in the common case where the query returns
334 ** just one row.
335 */
336 db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
337 while( db_step(&q)==SQLITE_ROW ){
338 int tid = db_column_int(&q, 0);
339 if( prevTid ) after_dephantomize(prevTid, 1);
340 prevTid = tid;
341 }
342 db_finalize(&q);
343 if( prevTid ) after_dephantomize(prevTid, 1);
344 if( linkFlag ){
345 Blob content;
346 content_get(rid, &content);
347 manifest_crosslink(rid, &content);
348 blob_reset(&content);
349 }
 
 
 
 
 
 
 
 
350 }
351
352 /*
353 ** Write content into the database. Return the record ID. If the
354 ** content is already in the database, just return the record ID.
355
--- src/content.c
+++ src/content.c
@@ -19,33 +19,25 @@
19 */
20 #include "config.h"
21 #include "content.h"
22 #include <assert.h>
23
 
 
 
 
 
 
 
 
 
24 /*
25 ** The artifact retrival cache
26 */
 
 
27 static struct {
28 i64 szTotal; /* Total size of all entries in the cache */
29 int n; /* Current number of eache entries */
30 int nAlloc; /* Number of slots allocated in a[] */
31 int nextAge; /* Age counter for implementing LRU */
32 int skipCnt; /* Used to limit entries expelled from cache */
33 struct cacheLine { /* One instance of this for each cache entry */
34 int rid; /* Artifact id */
35 int age; /* Age. Newer is larger */
36 Blob content; /* Content of the artifact */
37 } *a; /* The positive cache */
38 Bag inCache; /* Set of artifacts currently in cache */
39
40 /*
41 ** The missing artifact cache.
42 **
43 ** Artifacts whose record ID are in missingCache cannot be retrieved
@@ -56,10 +48,54 @@
48 */
49 Bag missing; /* Cache of artifacts that are incomplete */
50 Bag available; /* Cache of artifacts that are complete */
51 } contentCache;
52
53 /*
54 ** Remove the oldest element from the content cache
55 */
56 static void content_cache_expire_oldest(void){
57 int i;
58 int mnAge = contentCache.nextAge;
59 int mn = -1;
60 for(i=0; i<contentCache.n; i++){
61 if( contentCache.a[i].age<mnAge ){
62 mnAge = contentCache.a[i].age;
63 mn = i;
64 }
65 }
66 if( mn>=0 ){
67 bag_remove(&contentCache.inCache, contentCache.a[mn].rid);
68 contentCache.szTotal -= blob_size(&contentCache.a[mn].content);
69 blob_reset(&contentCache.a[mn].content);
70 contentCache.n--;
71 contentCache.a[mn] = contentCache.a[contentCache.n];
72 }
73 }
74
75 /*
76 ** Add an entry to the content cache
77 */
78 void content_cache_insert(int rid, Blob *pBlob){
79 struct cacheLine *p;
80 if( contentCache.n>500 || contentCache.szTotal>50000000 ){
81 content_cache_expire_oldest();
82 }
83 if( contentCache.n>=contentCache.nAlloc ){
84 contentCache.nAlloc = contentCache.nAlloc*2 + 10;
85 contentCache.a = realloc(contentCache.a,
86 contentCache.nAlloc*sizeof(contentCache.a[0]));
87 if( contentCache.a==0 ) fossil_panic("out of memory");
88 }
89 p = &contentCache.a[contentCache.n++];
90 p->rid = rid;
91 p->age = contentCache.nextAge++;
92 contentCache.szTotal += blob_size(pBlob);
93 p->content = *pBlob;
94 blob_zero(pBlob);
95 bag_insert(&contentCache.inCache, rid);
96 }
97
98 /*
99 ** Clear the content cache.
100 */
101 void content_clear_cache(void){
@@ -67,11 +103,13 @@
103 for(i=0; i<contentCache.n; i++){
104 blob_reset(&contentCache.a[i].content);
105 }
106 bag_clear(&contentCache.missing);
107 bag_clear(&contentCache.available);
108 bag_clear(&contentCache.inCache);
109 contentCache.n = 0;
110 contentCache.szTotal = 0;
111 }
112
113 /*
114 ** Return the srcid associated with rid. Or return 0 if rid is
115 ** original content and not a delta.
@@ -95,32 +133,31 @@
133 ** true if it is. Return false if rid is a phantom or depends on
134 ** a phantom.
135 */
136 int content_is_available(int rid){
137 int srcid;
138 int depth = 0; /* Limit to recursion depth */
139 while( depth++ < 10000000 ){
140 if( bag_find(&contentCache.missing, rid) ){
141 return 0;
142 }
143 if( bag_find(&contentCache.available, rid) ){
144 return 1;
145 }
146 if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
147 bag_insert(&contentCache.missing, rid);
148 return 0;
149 }
150 srcid = findSrcid(rid);
151 if( srcid==0 ){
152 bag_insert(&contentCache.available, rid);
153 return 1;
154 }
155 rid = srcid;
156 }
157 fossil_panic("delta-loop in repository");
158 return 0;
 
159 }
160
161 /*
162 ** Mark artifact rid as being available now. Update the cache to
163 ** show that everything that was formerly unavailable because rid
@@ -163,113 +200,84 @@
200 }
201 db_reset(&q);
202 return rc;
203 }
204
 
205 /*
206 ** Extract the content for ID rid and put it into the
207 ** uninitialized blob. Return 1 on success. If the record
208 ** is a phantom, zero pBlob and return 0.
209 */
210 int content_get(int rid, Blob *pBlob){
211 int rc;
 
 
212 int i;
213 int nextRid;
214
215 assert( g.repositoryOpen );
216 blob_zero(pBlob);
217 if( rid==0 ) return 0;
218
219 /* Early out if we know the content is not available */
220 if( bag_find(&contentCache.missing, rid) ){
 
 
221 return 0;
222 }
223
224 /* Look for the artifact in the cache first */
225 if( bag_find(&contentCache.inCache, rid) ){
226 for(i=0; i<contentCache.n; i++){
227 if( contentCache.a[i].rid==rid ){
228 blob_copy(pBlob, &contentCache.a[i].content);
229 contentCache.a[i].age = contentCache.nextAge++;
230 return 1;
231 }
232 }
233 }
234
235 nextRid = findSrcid(rid);
236 if( nextRid==0 ){
237 rc = content_of_blob(rid, pBlob);
238 }else{
239 int n = 1;
240 int nAlloc = 10;
241 int *a = 0;
242 int mx;
243 Blob delta, next;
244
245 a = malloc( sizeof(a[0])*nAlloc );
246 if( a==0 ) fossil_panic("out of memory");
247 a[0] = rid;
248 a[1] = nextRid;
249 n = 1;
250 while( !bag_find(&contentCache.inCache, nextRid)
251 && (nextRid = findSrcid(nextRid))>0 ){
252 n++;
253 if( n>=nAlloc ){
254 nAlloc = nAlloc*2 + 10;
255 a = realloc(a, nAlloc*sizeof(a[0]));
256 if( a==0 ) fossil_panic("out of memory");
257 }
258 a[n] = nextRid;
259 }
260 mx = n;
261 rc = content_get(a[n], pBlob);
262 n--;
263 while( rc && n>=0 ){
264 rc = content_of_blob(a[n], &delta);
265 if( rc ){
266 blob_delta_apply(pBlob, &delta, &next);
267 blob_reset(&delta);
268 if( (mx-n)%8==0 ){
269 content_cache_insert(a[n+1], pBlob);
270 }else{
271 blob_reset(pBlob);
272 }
273 *pBlob = next;
274 }
275 n--;
276 }
277 free(a);
278 if( !rc ) blob_reset(pBlob);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279 }
280 if( rc==0 ){
281 bag_insert(&contentCache.missing, rid);
282 }else{
283 bag_insert(&contentCache.available, rid);
@@ -320,35 +328,45 @@
328
329 /*
330 ** When a record is converted from a phantom to a real record,
331 ** if that record has other records that are derived by delta,
332 ** then call manifest_crosslink() on those other records.
333 **
334 ** Tail recursion is used to minimize stack depth.
335 */
336 void after_dephantomize(int rid, int linkFlag){
337 Stmt q;
338 int nChildAlloc = 0;
339 int *aChild = 0;
340
341 while( rid ){
342 int nChildUsed = 0;
343 int i;
344 if( linkFlag ){
345 Blob content;
346 content_get(rid, &content);
347 manifest_crosslink(rid, &content);
348 blob_reset(&content);
349 }
350 db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
351 while( db_step(&q)==SQLITE_ROW ){
352 int child = db_column_int(&q, 0);
353 if( nChildUsed>=nChildAlloc ){
354 nChildAlloc = nChildAlloc*2 + 10;
355 aChild = realloc(aChild, nChildAlloc*sizeof(aChild));
356 if( aChild==0 ) fossil_panic("out of memory");
357 }
358 aChild[nChildUsed++] = child;
359 }
360 db_finalize(&q);
361 for(i=1; i<nChildUsed; i++){
362 after_dephantomize(aChild[i], 1);
363 }
364 rid = nChildUsed>0 ? aChild[0] : 0;
365 linkFlag = 1;
366 }
367 free(aChild);
368 }
369
370 /*
371 ** Write content into the database. Return the record ID. If the
372 ** content is already in the database, just return the record ID.
373
+160 -142
--- src/content.c
+++ src/content.c
@@ -19,33 +19,25 @@
1919
*/
2020
#include "config.h"
2121
#include "content.h"
2222
#include <assert.h>
2323
24
-/*
25
-** Macros for debugging
26
-*/
27
-#if 0
28
-# define CONTENT_TRACE(X) printf X;
29
-#else
30
-# define CONTENT_TRACE(X)
31
-#endif
32
-
3324
/*
3425
** The artifact retrival cache
3526
*/
36
-#define MX_CACHE_CNT 50 /* Maximum number of positive cache entries */
37
-#define EXPELL_INTERVAL 5 /* How often to expell from a full cache */
3827
static struct {
39
- int n; /* Current number of positive cache entries */
28
+ i64 szTotal; /* Total size of all entries in the cache */
29
+ int n; /* Current number of eache entries */
30
+ int nAlloc; /* Number of slots allocated in a[] */
4031
int nextAge; /* Age counter for implementing LRU */
4132
int skipCnt; /* Used to limit entries expelled from cache */
42
- struct { /* One instance of this for each cache entry */
33
+ struct cacheLine { /* One instance of this for each cache entry */
4334
int rid; /* Artifact id */
4435
int age; /* Age. Newer is larger */
4536
Blob content; /* Content of the artifact */
46
- } a[MX_CACHE_CNT]; /* The positive cache */
37
+ } *a; /* The positive cache */
38
+ Bag inCache; /* Set of artifacts currently in cache */
4739
4840
/*
4941
** The missing artifact cache.
5042
**
5143
** Artifacts whose record ID are in missingCache cannot be retrieved
@@ -56,10 +48,54 @@
5648
*/
5749
Bag missing; /* Cache of artifacts that are incomplete */
5850
Bag available; /* Cache of artifacts that are complete */
5951
} contentCache;
6052
53
+/*
54
+** Remove the oldest element from the content cache
55
+*/
56
+static void content_cache_expire_oldest(void){
57
+ int i;
58
+ int mnAge = contentCache.nextAge;
59
+ int mn = -1;
60
+ for(i=0; i<contentCache.n; i++){
61
+ if( contentCache.a[i].age<mnAge ){
62
+ mnAge = contentCache.a[i].age;
63
+ mn = i;
64
+ }
65
+ }
66
+ if( mn>=0 ){
67
+ bag_remove(&contentCache.inCache, contentCache.a[mn].rid);
68
+ contentCache.szTotal -= blob_size(&contentCache.a[mn].content);
69
+ blob_reset(&contentCache.a[mn].content);
70
+ contentCache.n--;
71
+ contentCache.a[mn] = contentCache.a[contentCache.n];
72
+ }
73
+}
74
+
75
+/*
76
+** Add an entry to the content cache
77
+*/
78
+void content_cache_insert(int rid, Blob *pBlob){
79
+ struct cacheLine *p;
80
+ if( contentCache.n>500 || contentCache.szTotal>50000000 ){
81
+ content_cache_expire_oldest();
82
+ }
83
+ if( contentCache.n>=contentCache.nAlloc ){
84
+ contentCache.nAlloc = contentCache.nAlloc*2 + 10;
85
+ contentCache.a = realloc(contentCache.a,
86
+ contentCache.nAlloc*sizeof(contentCache.a[0]));
87
+ if( contentCache.a==0 ) fossil_panic("out of memory");
88
+ }
89
+ p = &contentCache.a[contentCache.n++];
90
+ p->rid = rid;
91
+ p->age = contentCache.nextAge++;
92
+ contentCache.szTotal += blob_size(pBlob);
93
+ p->content = *pBlob;
94
+ blob_zero(pBlob);
95
+ bag_insert(&contentCache.inCache, rid);
96
+}
6197
6298
/*
6399
** Clear the content cache.
64100
*/
65101
void content_clear_cache(void){
@@ -67,11 +103,13 @@
67103
for(i=0; i<contentCache.n; i++){
68104
blob_reset(&contentCache.a[i].content);
69105
}
70106
bag_clear(&contentCache.missing);
71107
bag_clear(&contentCache.available);
108
+ bag_clear(&contentCache.inCache);
72109
contentCache.n = 0;
110
+ contentCache.szTotal = 0;
73111
}
74112
75113
/*
76114
** Return the srcid associated with rid. Or return 0 if rid is
77115
** original content and not a delta.
@@ -95,32 +133,31 @@
95133
** true if it is. Return false if rid is a phantom or depends on
96134
** a phantom.
97135
*/
98136
int content_is_available(int rid){
99137
int srcid;
100
- if( bag_find(&contentCache.missing, rid) ){
101
- return 0;
102
- }
103
- if( bag_find(&contentCache.available, rid) ){
104
- return 1;
105
- }
106
- if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
107
- bag_insert(&contentCache.missing, rid);
108
- return 0;
109
- }
110
- srcid = findSrcid(rid);
111
- if( srcid==0 ){
112
- bag_insert(&contentCache.available, rid);
113
- return 1;
114
- }
115
- if( content_is_available(srcid) ){
116
- bag_insert(&contentCache.available, rid);
117
- return 1;
118
- }else{
119
- bag_insert(&contentCache.missing, rid);
120
- return 0;
121
- }
138
+ int depth = 0; /* Limit to recursion depth */
139
+ while( depth++ < 10000000 ){
140
+ if( bag_find(&contentCache.missing, rid) ){
141
+ return 0;
142
+ }
143
+ if( bag_find(&contentCache.available, rid) ){
144
+ return 1;
145
+ }
146
+ if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
147
+ bag_insert(&contentCache.missing, rid);
148
+ return 0;
149
+ }
150
+ srcid = findSrcid(rid);
151
+ if( srcid==0 ){
152
+ bag_insert(&contentCache.available, rid);
153
+ return 1;
154
+ }
155
+ rid = srcid;
156
+ }
157
+ fossil_panic("delta-loop in repository");
158
+ return 0;
122159
}
123160
124161
/*
125162
** Mark artifact rid as being available now. Update the cache to
126163
** show that everything that was formerly unavailable because rid
@@ -163,113 +200,84 @@
163200
}
164201
db_reset(&q);
165202
return rc;
166203
}
167204
168
-
169205
/*
170206
** Extract the content for ID rid and put it into the
171207
** uninitialized blob. Return 1 on success. If the record
172208
** is a phantom, zero pBlob and return 0.
173209
*/
174210
int content_get(int rid, Blob *pBlob){
175
- Blob src;
176
- int srcid;
177
- int rc = 0;
211
+ int rc;
178212
int i;
179
- static Bag inProcess;
213
+ int nextRid;
180214
181215
assert( g.repositoryOpen );
182216
blob_zero(pBlob);
183217
if( rid==0 ) return 0;
184218
185219
/* Early out if we know the content is not available */
186220
if( bag_find(&contentCache.missing, rid) ){
187
- CONTENT_TRACE(("%*smiss from cache: %d\n",
188
- bag_count(&inProcess), "", rid))
189221
return 0;
190222
}
191223
192224
/* Look for the artifact in the cache first */
193
- for(i=0; i<contentCache.n; i++){
194
- if( contentCache.a[i].rid==rid ){
195
- *pBlob = contentCache.a[i].content;
196
- blob_zero(&contentCache.a[i].content);
197
- contentCache.n--;
198
- if( i<contentCache.n ){
199
- contentCache.a[i] = contentCache.a[contentCache.n];
200
- }
201
- CONTENT_TRACE(("%*scache: %d\n",
202
- bag_count(&inProcess), "", rid))
203
- return 1;
204
- }
205
- }
206
-
207
- /* See if we need to apply a delta to find this artifact */
208
- srcid = findSrcid(rid);
209
- CONTENT_TRACE(("%*ssearching for %d. Need %d.\n",
210
- bag_count(&inProcess), "", rid, srcid))
211
-
212
-
213
- if( srcid ){
214
- /* Yes, a delta is required */
215
- if( bag_find(&inProcess, srcid) ){
216
- db_multi_exec(
217
- "UPDATE blob SET content=NULL, size=-1 WHERE rid=%d;"
218
- "DELETE FROM delta WHERE rid=%d;"
219
- "INSERT OR IGNORE INTO phantom VALUES(%d);",
220
- srcid, srcid, srcid
221
- );
222
- blob_zero(pBlob);
223
- return 0;
224
- }
225
- bag_insert(&inProcess, srcid);
226
-
227
- if( content_get(srcid, &src) ){
228
- Blob delta;
229
- if( content_of_blob(rid, &delta) ){
230
- blob_init(pBlob,0,0);
231
- blob_delta_apply(&src, &delta, pBlob);
225
+ if( bag_find(&contentCache.inCache, rid) ){
226
+ for(i=0; i<contentCache.n; i++){
227
+ if( contentCache.a[i].rid==rid ){
228
+ blob_copy(pBlob, &contentCache.a[i].content);
229
+ contentCache.a[i].age = contentCache.nextAge++;
230
+ return 1;
231
+ }
232
+ }
233
+ }
234
+
235
+ nextRid = findSrcid(rid);
236
+ if( nextRid==0 ){
237
+ rc = content_of_blob(rid, pBlob);
238
+ }else{
239
+ int n = 1;
240
+ int nAlloc = 10;
241
+ int *a = 0;
242
+ int mx;
243
+ Blob delta, next;
244
+
245
+ a = malloc( sizeof(a[0])*nAlloc );
246
+ if( a==0 ) fossil_panic("out of memory");
247
+ a[0] = rid;
248
+ a[1] = nextRid;
249
+ n = 1;
250
+ while( !bag_find(&contentCache.inCache, nextRid)
251
+ && (nextRid = findSrcid(nextRid))>0 ){
252
+ n++;
253
+ if( n>=nAlloc ){
254
+ nAlloc = nAlloc*2 + 10;
255
+ a = realloc(a, nAlloc*sizeof(a[0]));
256
+ if( a==0 ) fossil_panic("out of memory");
257
+ }
258
+ a[n] = nextRid;
259
+ }
260
+ mx = n;
261
+ rc = content_get(a[n], pBlob);
262
+ n--;
263
+ while( rc && n>=0 ){
264
+ rc = content_of_blob(a[n], &delta);
265
+ if( rc ){
266
+ blob_delta_apply(pBlob, &delta, &next);
232267
blob_reset(&delta);
233
- rc = 1;
234
- }
235
-
236
- /* Save the srcid artifact in the cache */
237
- if( contentCache.n<MX_CACHE_CNT ){
238
- i = contentCache.n++;
239
- }else if( ((contentCache.skipCnt++)%EXPELL_INTERVAL)!=0 ){
240
- i = -1;
241
- }else{
242
- int j, best;
243
- best = contentCache.nextAge+1;
244
- i = -1;
245
- for(j=0; j<contentCache.n; j++){
246
- if( contentCache.a[j].age<best ){
247
- i = j;
248
- best = contentCache.a[j].age;
249
- }
250
- }
251
- CONTENT_TRACE(("%*sexpell %d from cache\n",
252
- bag_count(&inProcess), "", contentCache.a[i].rid))
253
- blob_reset(&contentCache.a[i].content);
254
- }
255
- if( i>=0 ){
256
- contentCache.a[i].content = src;
257
- contentCache.a[i].age = contentCache.nextAge++;
258
- contentCache.a[i].rid = srcid;
259
- CONTENT_TRACE(("%*sadd %d to cache\n",
260
- bag_count(&inProcess), "", srcid))
261
- }else{
262
- blob_reset(&src);
263
- }
264
- }
265
- bag_remove(&inProcess, srcid);
266
- }else{
267
- /* No delta required. Read content directly from the database */
268
- if( content_of_blob(rid, pBlob) ){
269
- rc = 1;
270
- }
268
+ if( (mx-n)%8==0 ){
269
+ content_cache_insert(a[n+1], pBlob);
270
+ }else{
271
+ blob_reset(pBlob);
272
+ }
273
+ *pBlob = next;
274
+ }
275
+ n--;
276
+ }
277
+ free(a);
278
+ if( !rc ) blob_reset(pBlob);
271279
}
272280
if( rc==0 ){
273281
bag_insert(&contentCache.missing, rid);
274282
}else{
275283
bag_insert(&contentCache.available, rid);
@@ -320,35 +328,45 @@
320328
321329
/*
322330
** When a record is converted from a phantom to a real record,
323331
** if that record has other records that are derived by delta,
324332
** then call manifest_crosslink() on those other records.
333
+**
334
+** Tail recursion is used to minimize stack depth.
325335
*/
326336
void after_dephantomize(int rid, int linkFlag){
327337
Stmt q;
328
- int prevTid = 0;
329
-
330
- /* The prevTid variable is used to delay invoking this routine
331
- ** recursively, if possible, until after the query has finalized,
332
- ** in order to avoid having an excessive number of prepared statements.
333
- ** This is most effective in the common case where the query returns
334
- ** just one row.
335
- */
336
- db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
337
- while( db_step(&q)==SQLITE_ROW ){
338
- int tid = db_column_int(&q, 0);
339
- if( prevTid ) after_dephantomize(prevTid, 1);
340
- prevTid = tid;
341
- }
342
- db_finalize(&q);
343
- if( prevTid ) after_dephantomize(prevTid, 1);
344
- if( linkFlag ){
345
- Blob content;
346
- content_get(rid, &content);
347
- manifest_crosslink(rid, &content);
348
- blob_reset(&content);
349
- }
338
+ int nChildAlloc = 0;
339
+ int *aChild = 0;
340
+
341
+ while( rid ){
342
+ int nChildUsed = 0;
343
+ int i;
344
+ if( linkFlag ){
345
+ Blob content;
346
+ content_get(rid, &content);
347
+ manifest_crosslink(rid, &content);
348
+ blob_reset(&content);
349
+ }
350
+ db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
351
+ while( db_step(&q)==SQLITE_ROW ){
352
+ int child = db_column_int(&q, 0);
353
+ if( nChildUsed>=nChildAlloc ){
354
+ nChildAlloc = nChildAlloc*2 + 10;
355
+ aChild = realloc(aChild, nChildAlloc*sizeof(aChild));
356
+ if( aChild==0 ) fossil_panic("out of memory");
357
+ }
358
+ aChild[nChildUsed++] = child;
359
+ }
360
+ db_finalize(&q);
361
+ for(i=1; i<nChildUsed; i++){
362
+ after_dephantomize(aChild[i], 1);
363
+ }
364
+ rid = nChildUsed>0 ? aChild[0] : 0;
365
+ linkFlag = 1;
366
+ }
367
+ free(aChild);
350368
}
351369
352370
/*
353371
** Write content into the database. Return the record ID. If the
354372
** content is already in the database, just return the record ID.
355373
--- src/content.c
+++ src/content.c
@@ -19,33 +19,25 @@
19 */
20 #include "config.h"
21 #include "content.h"
22 #include <assert.h>
23
24 /*
25 ** Macros for debugging
26 */
27 #if 0
28 # define CONTENT_TRACE(X) printf X;
29 #else
30 # define CONTENT_TRACE(X)
31 #endif
32
33 /*
34 ** The artifact retrival cache
35 */
36 #define MX_CACHE_CNT 50 /* Maximum number of positive cache entries */
37 #define EXPELL_INTERVAL 5 /* How often to expell from a full cache */
38 static struct {
39 int n; /* Current number of positive cache entries */
 
 
40 int nextAge; /* Age counter for implementing LRU */
41 int skipCnt; /* Used to limit entries expelled from cache */
42 struct { /* One instance of this for each cache entry */
43 int rid; /* Artifact id */
44 int age; /* Age. Newer is larger */
45 Blob content; /* Content of the artifact */
46 } a[MX_CACHE_CNT]; /* The positive cache */
 
47
48 /*
49 ** The missing artifact cache.
50 **
51 ** Artifacts whose record ID are in missingCache cannot be retrieved
@@ -56,10 +48,54 @@
56 */
57 Bag missing; /* Cache of artifacts that are incomplete */
58 Bag available; /* Cache of artifacts that are complete */
59 } contentCache;
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
62 /*
63 ** Clear the content cache.
64 */
65 void content_clear_cache(void){
@@ -67,11 +103,13 @@
67 for(i=0; i<contentCache.n; i++){
68 blob_reset(&contentCache.a[i].content);
69 }
70 bag_clear(&contentCache.missing);
71 bag_clear(&contentCache.available);
 
72 contentCache.n = 0;
 
73 }
74
75 /*
76 ** Return the srcid associated with rid. Or return 0 if rid is
77 ** original content and not a delta.
@@ -95,32 +133,31 @@
95 ** true if it is. Return false if rid is a phantom or depends on
96 ** a phantom.
97 */
98 int content_is_available(int rid){
99 int srcid;
100 if( bag_find(&contentCache.missing, rid) ){
101 return 0;
102 }
103 if( bag_find(&contentCache.available, rid) ){
104 return 1;
105 }
106 if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
107 bag_insert(&contentCache.missing, rid);
108 return 0;
109 }
110 srcid = findSrcid(rid);
111 if( srcid==0 ){
112 bag_insert(&contentCache.available, rid);
113 return 1;
114 }
115 if( content_is_available(srcid) ){
116 bag_insert(&contentCache.available, rid);
117 return 1;
118 }else{
119 bag_insert(&contentCache.missing, rid);
120 return 0;
121 }
122 }
123
124 /*
125 ** Mark artifact rid as being available now. Update the cache to
126 ** show that everything that was formerly unavailable because rid
@@ -163,113 +200,84 @@
163 }
164 db_reset(&q);
165 return rc;
166 }
167
168
169 /*
170 ** Extract the content for ID rid and put it into the
171 ** uninitialized blob. Return 1 on success. If the record
172 ** is a phantom, zero pBlob and return 0.
173 */
174 int content_get(int rid, Blob *pBlob){
175 Blob src;
176 int srcid;
177 int rc = 0;
178 int i;
179 static Bag inProcess;
180
181 assert( g.repositoryOpen );
182 blob_zero(pBlob);
183 if( rid==0 ) return 0;
184
185 /* Early out if we know the content is not available */
186 if( bag_find(&contentCache.missing, rid) ){
187 CONTENT_TRACE(("%*smiss from cache: %d\n",
188 bag_count(&inProcess), "", rid))
189 return 0;
190 }
191
192 /* Look for the artifact in the cache first */
193 for(i=0; i<contentCache.n; i++){
194 if( contentCache.a[i].rid==rid ){
195 *pBlob = contentCache.a[i].content;
196 blob_zero(&contentCache.a[i].content);
197 contentCache.n--;
198 if( i<contentCache.n ){
199 contentCache.a[i] = contentCache.a[contentCache.n];
200 }
201 CONTENT_TRACE(("%*scache: %d\n",
202 bag_count(&inProcess), "", rid))
203 return 1;
204 }
205 }
206
207 /* See if we need to apply a delta to find this artifact */
208 srcid = findSrcid(rid);
209 CONTENT_TRACE(("%*ssearching for %d. Need %d.\n",
210 bag_count(&inProcess), "", rid, srcid))
211
212
213 if( srcid ){
214 /* Yes, a delta is required */
215 if( bag_find(&inProcess, srcid) ){
216 db_multi_exec(
217 "UPDATE blob SET content=NULL, size=-1 WHERE rid=%d;"
218 "DELETE FROM delta WHERE rid=%d;"
219 "INSERT OR IGNORE INTO phantom VALUES(%d);",
220 srcid, srcid, srcid
221 );
222 blob_zero(pBlob);
223 return 0;
224 }
225 bag_insert(&inProcess, srcid);
226
227 if( content_get(srcid, &src) ){
228 Blob delta;
229 if( content_of_blob(rid, &delta) ){
230 blob_init(pBlob,0,0);
231 blob_delta_apply(&src, &delta, pBlob);
 
 
 
232 blob_reset(&delta);
233 rc = 1;
234 }
235
236 /* Save the srcid artifact in the cache */
237 if( contentCache.n<MX_CACHE_CNT ){
238 i = contentCache.n++;
239 }else if( ((contentCache.skipCnt++)%EXPELL_INTERVAL)!=0 ){
240 i = -1;
241 }else{
242 int j, best;
243 best = contentCache.nextAge+1;
244 i = -1;
245 for(j=0; j<contentCache.n; j++){
246 if( contentCache.a[j].age<best ){
247 i = j;
248 best = contentCache.a[j].age;
249 }
250 }
251 CONTENT_TRACE(("%*sexpell %d from cache\n",
252 bag_count(&inProcess), "", contentCache.a[i].rid))
253 blob_reset(&contentCache.a[i].content);
254 }
255 if( i>=0 ){
256 contentCache.a[i].content = src;
257 contentCache.a[i].age = contentCache.nextAge++;
258 contentCache.a[i].rid = srcid;
259 CONTENT_TRACE(("%*sadd %d to cache\n",
260 bag_count(&inProcess), "", srcid))
261 }else{
262 blob_reset(&src);
263 }
264 }
265 bag_remove(&inProcess, srcid);
266 }else{
267 /* No delta required. Read content directly from the database */
268 if( content_of_blob(rid, pBlob) ){
269 rc = 1;
270 }
271 }
272 if( rc==0 ){
273 bag_insert(&contentCache.missing, rid);
274 }else{
275 bag_insert(&contentCache.available, rid);
@@ -320,35 +328,45 @@
320
321 /*
322 ** When a record is converted from a phantom to a real record,
323 ** if that record has other records that are derived by delta,
324 ** then call manifest_crosslink() on those other records.
 
 
325 */
326 void after_dephantomize(int rid, int linkFlag){
327 Stmt q;
328 int prevTid = 0;
329
330 /* The prevTid variable is used to delay invoking this routine
331 ** recursively, if possible, until after the query has finalized,
332 ** in order to avoid having an excessive number of prepared statements.
333 ** This is most effective in the common case where the query returns
334 ** just one row.
335 */
336 db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
337 while( db_step(&q)==SQLITE_ROW ){
338 int tid = db_column_int(&q, 0);
339 if( prevTid ) after_dephantomize(prevTid, 1);
340 prevTid = tid;
341 }
342 db_finalize(&q);
343 if( prevTid ) after_dephantomize(prevTid, 1);
344 if( linkFlag ){
345 Blob content;
346 content_get(rid, &content);
347 manifest_crosslink(rid, &content);
348 blob_reset(&content);
349 }
 
 
 
 
 
 
 
 
350 }
351
352 /*
353 ** Write content into the database. Return the record ID. If the
354 ** content is already in the database, just return the record ID.
355
--- src/content.c
+++ src/content.c
@@ -19,33 +19,25 @@
19 */
20 #include "config.h"
21 #include "content.h"
22 #include <assert.h>
23
 
 
 
 
 
 
 
 
 
24 /*
25 ** The artifact retrival cache
26 */
 
 
27 static struct {
28 i64 szTotal; /* Total size of all entries in the cache */
29 int n; /* Current number of eache entries */
30 int nAlloc; /* Number of slots allocated in a[] */
31 int nextAge; /* Age counter for implementing LRU */
32 int skipCnt; /* Used to limit entries expelled from cache */
33 struct cacheLine { /* One instance of this for each cache entry */
34 int rid; /* Artifact id */
35 int age; /* Age. Newer is larger */
36 Blob content; /* Content of the artifact */
37 } *a; /* The positive cache */
38 Bag inCache; /* Set of artifacts currently in cache */
39
40 /*
41 ** The missing artifact cache.
42 **
43 ** Artifacts whose record ID are in missingCache cannot be retrieved
@@ -56,10 +48,54 @@
48 */
49 Bag missing; /* Cache of artifacts that are incomplete */
50 Bag available; /* Cache of artifacts that are complete */
51 } contentCache;
52
53 /*
54 ** Remove the oldest element from the content cache
55 */
56 static void content_cache_expire_oldest(void){
57 int i;
58 int mnAge = contentCache.nextAge;
59 int mn = -1;
60 for(i=0; i<contentCache.n; i++){
61 if( contentCache.a[i].age<mnAge ){
62 mnAge = contentCache.a[i].age;
63 mn = i;
64 }
65 }
66 if( mn>=0 ){
67 bag_remove(&contentCache.inCache, contentCache.a[mn].rid);
68 contentCache.szTotal -= blob_size(&contentCache.a[mn].content);
69 blob_reset(&contentCache.a[mn].content);
70 contentCache.n--;
71 contentCache.a[mn] = contentCache.a[contentCache.n];
72 }
73 }
74
75 /*
76 ** Add an entry to the content cache
77 */
78 void content_cache_insert(int rid, Blob *pBlob){
79 struct cacheLine *p;
80 if( contentCache.n>500 || contentCache.szTotal>50000000 ){
81 content_cache_expire_oldest();
82 }
83 if( contentCache.n>=contentCache.nAlloc ){
84 contentCache.nAlloc = contentCache.nAlloc*2 + 10;
85 contentCache.a = realloc(contentCache.a,
86 contentCache.nAlloc*sizeof(contentCache.a[0]));
87 if( contentCache.a==0 ) fossil_panic("out of memory");
88 }
89 p = &contentCache.a[contentCache.n++];
90 p->rid = rid;
91 p->age = contentCache.nextAge++;
92 contentCache.szTotal += blob_size(pBlob);
93 p->content = *pBlob;
94 blob_zero(pBlob);
95 bag_insert(&contentCache.inCache, rid);
96 }
97
98 /*
99 ** Clear the content cache.
100 */
101 void content_clear_cache(void){
@@ -67,11 +103,13 @@
103 for(i=0; i<contentCache.n; i++){
104 blob_reset(&contentCache.a[i].content);
105 }
106 bag_clear(&contentCache.missing);
107 bag_clear(&contentCache.available);
108 bag_clear(&contentCache.inCache);
109 contentCache.n = 0;
110 contentCache.szTotal = 0;
111 }
112
113 /*
114 ** Return the srcid associated with rid. Or return 0 if rid is
115 ** original content and not a delta.
@@ -95,32 +133,31 @@
133 ** true if it is. Return false if rid is a phantom or depends on
134 ** a phantom.
135 */
136 int content_is_available(int rid){
137 int srcid;
138 int depth = 0; /* Limit to recursion depth */
139 while( depth++ < 10000000 ){
140 if( bag_find(&contentCache.missing, rid) ){
141 return 0;
142 }
143 if( bag_find(&contentCache.available, rid) ){
144 return 1;
145 }
146 if( db_int(-1, "SELECT size FROM blob WHERE rid=%d", rid)<0 ){
147 bag_insert(&contentCache.missing, rid);
148 return 0;
149 }
150 srcid = findSrcid(rid);
151 if( srcid==0 ){
152 bag_insert(&contentCache.available, rid);
153 return 1;
154 }
155 rid = srcid;
156 }
157 fossil_panic("delta-loop in repository");
158 return 0;
 
159 }
160
161 /*
162 ** Mark artifact rid as being available now. Update the cache to
163 ** show that everything that was formerly unavailable because rid
@@ -163,113 +200,84 @@
200 }
201 db_reset(&q);
202 return rc;
203 }
204
 
205 /*
206 ** Extract the content for ID rid and put it into the
207 ** uninitialized blob. Return 1 on success. If the record
208 ** is a phantom, zero pBlob and return 0.
209 */
210 int content_get(int rid, Blob *pBlob){
211 int rc;
 
 
212 int i;
213 int nextRid;
214
215 assert( g.repositoryOpen );
216 blob_zero(pBlob);
217 if( rid==0 ) return 0;
218
219 /* Early out if we know the content is not available */
220 if( bag_find(&contentCache.missing, rid) ){
 
 
221 return 0;
222 }
223
224 /* Look for the artifact in the cache first */
225 if( bag_find(&contentCache.inCache, rid) ){
226 for(i=0; i<contentCache.n; i++){
227 if( contentCache.a[i].rid==rid ){
228 blob_copy(pBlob, &contentCache.a[i].content);
229 contentCache.a[i].age = contentCache.nextAge++;
230 return 1;
231 }
232 }
233 }
234
235 nextRid = findSrcid(rid);
236 if( nextRid==0 ){
237 rc = content_of_blob(rid, pBlob);
238 }else{
239 int n = 1;
240 int nAlloc = 10;
241 int *a = 0;
242 int mx;
243 Blob delta, next;
244
245 a = malloc( sizeof(a[0])*nAlloc );
246 if( a==0 ) fossil_panic("out of memory");
247 a[0] = rid;
248 a[1] = nextRid;
249 n = 1;
250 while( !bag_find(&contentCache.inCache, nextRid)
251 && (nextRid = findSrcid(nextRid))>0 ){
252 n++;
253 if( n>=nAlloc ){
254 nAlloc = nAlloc*2 + 10;
255 a = realloc(a, nAlloc*sizeof(a[0]));
256 if( a==0 ) fossil_panic("out of memory");
257 }
258 a[n] = nextRid;
259 }
260 mx = n;
261 rc = content_get(a[n], pBlob);
262 n--;
263 while( rc && n>=0 ){
264 rc = content_of_blob(a[n], &delta);
265 if( rc ){
266 blob_delta_apply(pBlob, &delta, &next);
267 blob_reset(&delta);
268 if( (mx-n)%8==0 ){
269 content_cache_insert(a[n+1], pBlob);
270 }else{
271 blob_reset(pBlob);
272 }
273 *pBlob = next;
274 }
275 n--;
276 }
277 free(a);
278 if( !rc ) blob_reset(pBlob);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279 }
280 if( rc==0 ){
281 bag_insert(&contentCache.missing, rid);
282 }else{
283 bag_insert(&contentCache.available, rid);
@@ -320,35 +328,45 @@
328
329 /*
330 ** When a record is converted from a phantom to a real record,
331 ** if that record has other records that are derived by delta,
332 ** then call manifest_crosslink() on those other records.
333 **
334 ** Tail recursion is used to minimize stack depth.
335 */
336 void after_dephantomize(int rid, int linkFlag){
337 Stmt q;
338 int nChildAlloc = 0;
339 int *aChild = 0;
340
341 while( rid ){
342 int nChildUsed = 0;
343 int i;
344 if( linkFlag ){
345 Blob content;
346 content_get(rid, &content);
347 manifest_crosslink(rid, &content);
348 blob_reset(&content);
349 }
350 db_prepare(&q, "SELECT rid FROM delta WHERE srcid=%d", rid);
351 while( db_step(&q)==SQLITE_ROW ){
352 int child = db_column_int(&q, 0);
353 if( nChildUsed>=nChildAlloc ){
354 nChildAlloc = nChildAlloc*2 + 10;
355 aChild = realloc(aChild, nChildAlloc*sizeof(aChild));
356 if( aChild==0 ) fossil_panic("out of memory");
357 }
358 aChild[nChildUsed++] = child;
359 }
360 db_finalize(&q);
361 for(i=1; i<nChildUsed; i++){
362 after_dephantomize(aChild[i], 1);
363 }
364 rid = nChildUsed>0 ? aChild[0] : 0;
365 linkFlag = 1;
366 }
367 free(aChild);
368 }
369
370 /*
371 ** Write content into the database. Return the record ID. If the
372 ** content is already in the database, just return the record ID.
373
+72 -66
--- src/rebuild.c
+++ src/rebuild.c
@@ -121,76 +121,82 @@
121121
Bag children;
122122
Blob copy;
123123
Blob *pUse;
124124
int nChild, i, cid;
125125
126
- /* Fix up the "blob.size" field if needed. */
127
- if( size!=blob_size(pBase) ){
128
- db_multi_exec(
129
- "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid
130
- );
131
- }
132
-
133
- /* Find all children of artifact rid */
134
- db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid");
135
- db_bind_int(&q1, ":rid", rid);
136
- bag_init(&children);
137
- while( db_step(&q1)==SQLITE_ROW ){
138
- int cid = db_column_int(&q1, 0);
139
- if( !bag_find(&bagDone, cid) ){
140
- bag_insert(&children, cid);
141
- }
142
- }
143
- nChild = bag_count(&children);
144
- db_reset(&q1);
145
-
146
- /* Crosslink the artifact */
147
- if( nChild==0 ){
148
- pUse = pBase;
149
- }else{
150
- blob_copy(&copy, pBase);
151
- pUse = &copy;
152
- }
153
- if( zFNameFormat==0 ){
154
- /* We are doing "fossil rebuild" */
155
- manifest_crosslink(rid, pUse);
156
- }else{
157
- /* We are doing "fossil deconstruct" */
158
- char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
159
- char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength);
160
- blob_write_to_file(pUse,zFile);
161
- free(zFile);
162
- free(zUuid);
163
- }
164
- blob_reset(pUse);
165
- rebuild_step_done(rid);
166
-
167
- /* Call all children recursively */
168
- for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
169
- Stmt q2;
170
- int sz;
171
- if( nChild==i ){
172
- pUse = pBase;
173
- }else{
174
- blob_copy(&copy, pBase);
175
- pUse = &copy;
176
- }
177
- db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid);
178
- if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
179
- Blob delta;
180
- db_ephemeral_blob(&q2, 0, &delta);
181
- blob_uncompress(&delta, &delta);
182
- blob_delta_apply(pUse, &delta, pUse);
183
- blob_reset(&delta);
184
- db_finalize(&q2);
185
- rebuild_step(cid, sz, pUse);
186
- }else{
187
- db_finalize(&q2);
188
- blob_reset(pUse);
189
- }
190
- }
191
- bag_clear(&children);
126
+ while( rid>0 ){
127
+
128
+ /* Fix up the "blob.size" field if needed. */
129
+ if( size!=blob_size(pBase) ){
130
+ db_multi_exec(
131
+ "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid
132
+ );
133
+ }
134
+
135
+ /* Find all children of artifact rid */
136
+ db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid");
137
+ db_bind_int(&q1, ":rid", rid);
138
+ bag_init(&children);
139
+ while( db_step(&q1)==SQLITE_ROW ){
140
+ int cid = db_column_int(&q1, 0);
141
+ if( !bag_find(&bagDone, cid) ){
142
+ bag_insert(&children, cid);
143
+ }
144
+ }
145
+ nChild = bag_count(&children);
146
+ db_reset(&q1);
147
+
148
+ /* Crosslink the artifact */
149
+ if( nChild==0 ){
150
+ pUse = pBase;
151
+ }else{
152
+ blob_copy(&copy, pBase);
153
+ pUse = &copy;
154
+ }
155
+ if( zFNameFormat==0 ){
156
+ /* We are doing "fossil rebuild" */
157
+ manifest_crosslink(rid, pUse);
158
+ }else{
159
+ /* We are doing "fossil deconstruct" */
160
+ char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
161
+ char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength);
162
+ blob_write_to_file(pUse,zFile);
163
+ free(zFile);
164
+ free(zUuid);
165
+ }
166
+ blob_reset(pUse);
167
+ rebuild_step_done(rid);
168
+
169
+ /* Call all children recursively */
170
+ rid = 0;
171
+ for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
172
+ Stmt q2;
173
+ int sz;
174
+ db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid);
175
+ if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
176
+ Blob delta, next;
177
+ db_ephemeral_blob(&q2, 0, &delta);
178
+ blob_uncompress(&delta, &delta);
179
+ blob_delta_apply(pBase, &delta, &next);
180
+ blob_reset(&delta);
181
+ db_finalize(&q2);
182
+ if( i<nChild ){
183
+ rebuild_step(cid, sz, &next);
184
+ }else{
185
+ /* Tail recursion */
186
+ rid = cid;
187
+ size = sz;
188
+ blob_reset(pBase);
189
+ *pBase = next;
190
+ }
191
+ }else{
192
+ db_finalize(&q2);
193
+ blob_reset(pBase);
194
+ }
195
+ }
196
+ bag_clear(&children);
197
+ }
192198
}
193199
194200
/*
195201
** Check to see if the "sym-trunk" tag exists. If not, create it
196202
** and attach it to the very first check-in.
197203
--- src/rebuild.c
+++ src/rebuild.c
@@ -121,76 +121,82 @@
121 Bag children;
122 Blob copy;
123 Blob *pUse;
124 int nChild, i, cid;
125
126 /* Fix up the "blob.size" field if needed. */
127 if( size!=blob_size(pBase) ){
128 db_multi_exec(
129 "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid
130 );
131 }
132
133 /* Find all children of artifact rid */
134 db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid");
135 db_bind_int(&q1, ":rid", rid);
136 bag_init(&children);
137 while( db_step(&q1)==SQLITE_ROW ){
138 int cid = db_column_int(&q1, 0);
139 if( !bag_find(&bagDone, cid) ){
140 bag_insert(&children, cid);
141 }
142 }
143 nChild = bag_count(&children);
144 db_reset(&q1);
145
146 /* Crosslink the artifact */
147 if( nChild==0 ){
148 pUse = pBase;
149 }else{
150 blob_copy(&copy, pBase);
151 pUse = &copy;
152 }
153 if( zFNameFormat==0 ){
154 /* We are doing "fossil rebuild" */
155 manifest_crosslink(rid, pUse);
156 }else{
157 /* We are doing "fossil deconstruct" */
158 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
159 char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength);
160 blob_write_to_file(pUse,zFile);
161 free(zFile);
162 free(zUuid);
163 }
164 blob_reset(pUse);
165 rebuild_step_done(rid);
166
167 /* Call all children recursively */
168 for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
169 Stmt q2;
170 int sz;
171 if( nChild==i ){
172 pUse = pBase;
173 }else{
174 blob_copy(&copy, pBase);
175 pUse = &copy;
176 }
177 db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid);
178 if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
179 Blob delta;
180 db_ephemeral_blob(&q2, 0, &delta);
181 blob_uncompress(&delta, &delta);
182 blob_delta_apply(pUse, &delta, pUse);
183 blob_reset(&delta);
184 db_finalize(&q2);
185 rebuild_step(cid, sz, pUse);
186 }else{
187 db_finalize(&q2);
188 blob_reset(pUse);
189 }
190 }
191 bag_clear(&children);
 
 
 
 
 
 
192 }
193
194 /*
195 ** Check to see if the "sym-trunk" tag exists. If not, create it
196 ** and attach it to the very first check-in.
197
--- src/rebuild.c
+++ src/rebuild.c
@@ -121,76 +121,82 @@
121 Bag children;
122 Blob copy;
123 Blob *pUse;
124 int nChild, i, cid;
125
126 while( rid>0 ){
127
128 /* Fix up the "blob.size" field if needed. */
129 if( size!=blob_size(pBase) ){
130 db_multi_exec(
131 "UPDATE blob SET size=%d WHERE rid=%d", blob_size(pBase), rid
132 );
133 }
134
135 /* Find all children of artifact rid */
136 db_static_prepare(&q1, "SELECT rid FROM delta WHERE srcid=:rid");
137 db_bind_int(&q1, ":rid", rid);
138 bag_init(&children);
139 while( db_step(&q1)==SQLITE_ROW ){
140 int cid = db_column_int(&q1, 0);
141 if( !bag_find(&bagDone, cid) ){
142 bag_insert(&children, cid);
143 }
144 }
145 nChild = bag_count(&children);
146 db_reset(&q1);
147
148 /* Crosslink the artifact */
149 if( nChild==0 ){
150 pUse = pBase;
151 }else{
152 blob_copy(&copy, pBase);
153 pUse = &copy;
154 }
155 if( zFNameFormat==0 ){
156 /* We are doing "fossil rebuild" */
157 manifest_crosslink(rid, pUse);
158 }else{
159 /* We are doing "fossil deconstruct" */
160 char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
161 char *zFile = mprintf(zFNameFormat, zUuid, zUuid+prefixLength);
162 blob_write_to_file(pUse,zFile);
163 free(zFile);
164 free(zUuid);
165 }
166 blob_reset(pUse);
167 rebuild_step_done(rid);
168
169 /* Call all children recursively */
170 rid = 0;
171 for(cid=bag_first(&children), i=1; cid; cid=bag_next(&children, cid), i++){
172 Stmt q2;
173 int sz;
174 db_prepare(&q2, "SELECT content, size FROM blob WHERE rid=%d", cid);
175 if( db_step(&q2)==SQLITE_ROW && (sz = db_column_int(&q2,1))>=0 ){
176 Blob delta, next;
177 db_ephemeral_blob(&q2, 0, &delta);
178 blob_uncompress(&delta, &delta);
179 blob_delta_apply(pBase, &delta, &next);
180 blob_reset(&delta);
181 db_finalize(&q2);
182 if( i<nChild ){
183 rebuild_step(cid, sz, &next);
184 }else{
185 /* Tail recursion */
186 rid = cid;
187 size = sz;
188 blob_reset(pBase);
189 *pBase = next;
190 }
191 }else{
192 db_finalize(&q2);
193 blob_reset(pBase);
194 }
195 }
196 bag_clear(&children);
197 }
198 }
199
200 /*
201 ** Check to see if the "sym-trunk" tag exists. If not, create it
202 ** and attach it to the very first check-in.
203
+4 -2
--- src/xfer.c
+++ src/xfer.c
@@ -119,22 +119,24 @@
119119
if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
120120
/* Ignore files that have been shunned */
121121
return;
122122
}
123123
if( pXfer->nToken==4 ){
124
- Blob src;
124
+ Blob src, next;
125125
srcid = rid_from_uuid(&pXfer->aToken[2], 1);
126126
if( content_get(srcid, &src)==0 ){
127127
rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid);
128128
pXfer->nDanglingFile++;
129129
db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid);
130130
content_make_public(rid);
131131
return;
132132
}
133133
pXfer->nDeltaRcvd++;
134
- blob_delta_apply(&src, &content, &content);
134
+ blob_delta_apply(&src, &content, &next);
135135
blob_reset(&src);
136
+ blob_reset(&content);
137
+ content = next;
136138
}else{
137139
pXfer->nFileRcvd++;
138140
}
139141
sha1sum_blob(&content, &hash);
140142
if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){
141143
--- src/xfer.c
+++ src/xfer.c
@@ -119,22 +119,24 @@
119 if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
120 /* Ignore files that have been shunned */
121 return;
122 }
123 if( pXfer->nToken==4 ){
124 Blob src;
125 srcid = rid_from_uuid(&pXfer->aToken[2], 1);
126 if( content_get(srcid, &src)==0 ){
127 rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid);
128 pXfer->nDanglingFile++;
129 db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid);
130 content_make_public(rid);
131 return;
132 }
133 pXfer->nDeltaRcvd++;
134 blob_delta_apply(&src, &content, &content);
135 blob_reset(&src);
 
 
136 }else{
137 pXfer->nFileRcvd++;
138 }
139 sha1sum_blob(&content, &hash);
140 if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){
141
--- src/xfer.c
+++ src/xfer.c
@@ -119,22 +119,24 @@
119 if( uuid_is_shunned(blob_str(&pXfer->aToken[1])) ){
120 /* Ignore files that have been shunned */
121 return;
122 }
123 if( pXfer->nToken==4 ){
124 Blob src, next;
125 srcid = rid_from_uuid(&pXfer->aToken[2], 1);
126 if( content_get(srcid, &src)==0 ){
127 rid = content_put(&content, blob_str(&pXfer->aToken[1]), srcid);
128 pXfer->nDanglingFile++;
129 db_multi_exec("DELETE FROM phantom WHERE rid=%d", rid);
130 content_make_public(rid);
131 return;
132 }
133 pXfer->nDeltaRcvd++;
134 blob_delta_apply(&src, &content, &next);
135 blob_reset(&src);
136 blob_reset(&content);
137 content = next;
138 }else{
139 pXfer->nFileRcvd++;
140 }
141 sha1sum_blob(&content, &hash);
142 if( !blob_eq_str(&pXfer->aToken[1], blob_str(&hash), -1) ){
143

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button