@@ -163,6 +163,141 @@ static NORETURN void die_invalid_line(const char *path,
163163
164164}
165165
166+ /*
167+ * An iterator over a packed-refs file that is currently mmapped.
168+ */
169+ struct mmapped_ref_iterator {
170+ struct ref_iterator base ;
171+
172+ struct packed_ref_cache * packed_refs ;
173+
174+ /* The current position in the mmapped file: */
175+ const char * pos ;
176+
177+ /* The end of the mmapped file: */
178+ const char * eof ;
179+
180+ struct object_id oid , peeled ;
181+
182+ struct strbuf refname_buf ;
183+ };
184+
185+ static int mmapped_ref_iterator_advance (struct ref_iterator * ref_iterator )
186+ {
187+ struct mmapped_ref_iterator * iter =
188+ (struct mmapped_ref_iterator * )ref_iterator ;
189+ const char * p = iter -> pos , * eol ;
190+
191+ strbuf_reset (& iter -> refname_buf );
192+
193+ if (iter -> pos == iter -> eof )
194+ return ref_iterator_abort (ref_iterator );
195+
196+ iter -> base .flags = REF_ISPACKED ;
197+
198+ if (iter -> eof - p < GIT_SHA1_HEXSZ + 2 ||
199+ parse_oid_hex (p , & iter -> oid , & p ) ||
200+ !isspace (* p ++ ))
201+ die_invalid_line (iter -> packed_refs -> refs -> path ,
202+ iter -> pos , iter -> eof - iter -> pos );
203+
204+ eol = memchr (p , '\n' , iter -> eof - p );
205+ if (!eol )
206+ die_unterminated_line (iter -> packed_refs -> refs -> path ,
207+ iter -> pos , iter -> eof - iter -> pos );
208+
209+ strbuf_add (& iter -> refname_buf , p , eol - p );
210+ iter -> base .refname = iter -> refname_buf .buf ;
211+
212+ if (check_refname_format (iter -> base .refname , REFNAME_ALLOW_ONELEVEL )) {
213+ if (!refname_is_safe (iter -> base .refname ))
214+ die ("packed refname is dangerous: %s" ,
215+ iter -> base .refname );
216+ oidclr (& iter -> oid );
217+ iter -> base .flags |= REF_BAD_NAME | REF_ISBROKEN ;
218+ }
219+ if (iter -> packed_refs -> peeled == PEELED_FULLY ||
220+ (iter -> packed_refs -> peeled == PEELED_TAGS &&
221+ starts_with (iter -> base .refname , "refs/tags/" )))
222+ iter -> base .flags |= REF_KNOWS_PEELED ;
223+
224+ iter -> pos = eol + 1 ;
225+
226+ if (iter -> pos < iter -> eof && * iter -> pos == '^' ) {
227+ p = iter -> pos + 1 ;
228+ if (iter -> eof - p < GIT_SHA1_HEXSZ + 1 ||
229+ parse_oid_hex (p , & iter -> peeled , & p ) ||
230+ * p ++ != '\n' )
231+ die_invalid_line (iter -> packed_refs -> refs -> path ,
232+ iter -> pos , iter -> eof - iter -> pos );
233+ iter -> pos = p ;
234+
235+ /*
236+ * Regardless of what the file header said, we
237+ * definitely know the value of *this* reference:
238+ */
239+ iter -> base .flags |= REF_KNOWS_PEELED ;
240+ } else {
241+ oidclr (& iter -> peeled );
242+ }
243+
244+ return ITER_OK ;
245+ }
246+
247+ static int mmapped_ref_iterator_peel (struct ref_iterator * ref_iterator ,
248+ struct object_id * peeled )
249+ {
250+ struct mmapped_ref_iterator * iter =
251+ (struct mmapped_ref_iterator * )ref_iterator ;
252+
253+ if ((iter -> base .flags & REF_KNOWS_PEELED )) {
254+ oidcpy (peeled , & iter -> peeled );
255+ return is_null_oid (& iter -> peeled ) ? -1 : 0 ;
256+ } else if ((iter -> base .flags & (REF_ISBROKEN | REF_ISSYMREF ))) {
257+ return -1 ;
258+ } else {
259+ return !!peel_object (iter -> oid .hash , peeled -> hash );
260+ }
261+ }
262+
263+ static int mmapped_ref_iterator_abort (struct ref_iterator * ref_iterator )
264+ {
265+ struct mmapped_ref_iterator * iter =
266+ (struct mmapped_ref_iterator * )ref_iterator ;
267+
268+ release_packed_ref_cache (iter -> packed_refs );
269+ strbuf_release (& iter -> refname_buf );
270+ base_ref_iterator_free (ref_iterator );
271+ return ITER_DONE ;
272+ }
273+
274+ static struct ref_iterator_vtable mmapped_ref_iterator_vtable = {
275+ mmapped_ref_iterator_advance ,
276+ mmapped_ref_iterator_peel ,
277+ mmapped_ref_iterator_abort
278+ };
279+
280+ struct ref_iterator * mmapped_ref_iterator_begin (
281+ const char * packed_refs_file ,
282+ struct packed_ref_cache * packed_refs ,
283+ const char * pos , const char * eof )
284+ {
285+ struct mmapped_ref_iterator * iter = xcalloc (1 , sizeof (* iter ));
286+ struct ref_iterator * ref_iterator = & iter -> base ;
287+
288+ base_ref_iterator_init (ref_iterator , & mmapped_ref_iterator_vtable , 0 );
289+
290+ iter -> packed_refs = packed_refs ;
291+ acquire_packed_ref_cache (iter -> packed_refs );
292+ iter -> pos = pos ;
293+ iter -> eof = eof ;
294+ strbuf_init (& iter -> refname_buf , 0 );
295+
296+ iter -> base .oid = & iter -> oid ;
297+
298+ return ref_iterator ;
299+ }
300+
166301/*
167302 * Read from the `packed-refs` file into a newly-allocated
168303 * `packed_ref_cache` and return it. The return value will already
@@ -199,9 +334,10 @@ static struct packed_ref_cache *read_packed_refs(struct packed_ref_store *refs)
199334 struct stat st ;
200335 size_t size ;
201336 char * buf ;
202- const char * pos , * eol , * eof ;
203- struct strbuf tmp = STRBUF_INIT ;
337+ const char * pos , * eof ;
204338 struct ref_dir * dir ;
339+ struct ref_iterator * iter ;
340+ int ok ;
205341
206342 packed_refs -> refs = refs ;
207343 acquire_packed_ref_cache (packed_refs );
@@ -235,7 +371,9 @@ static struct packed_ref_cache *read_packed_refs(struct packed_ref_store *refs)
235371
236372 /* If the file has a header line, process it: */
237373 if (pos < eof && * pos == '#' ) {
374+ struct strbuf tmp = STRBUF_INIT ;
238375 char * p ;
376+ const char * eol ;
239377 struct string_list traits = STRING_LIST_INIT_NODUP ;
240378
241379 eol = memchr (pos , '\n' , eof - pos );
@@ -259,69 +397,28 @@ static struct packed_ref_cache *read_packed_refs(struct packed_ref_store *refs)
259397 pos = eol + 1 ;
260398
261399 string_list_clear (& traits , 0 );
262- strbuf_reset (& tmp );
400+ strbuf_release (& tmp );
263401 }
264402
265403 dir = get_ref_dir (packed_refs -> cache -> root );
266- while (pos < eof ) {
267- const char * p = pos ;
268- struct object_id oid ;
269- const char * refname ;
270- int flag = REF_ISPACKED ;
271- struct ref_entry * entry = NULL ;
272-
273- if (eof - pos < GIT_SHA1_HEXSZ + 2 ||
274- parse_oid_hex (p , & oid , & p ) ||
275- !isspace (* p ++ ))
276- die_invalid_line (refs -> path , pos , eof - pos );
404+ iter = mmapped_ref_iterator_begin (refs -> path , packed_refs , pos , eof );
405+ while ((ok = ref_iterator_advance (iter )) == ITER_OK ) {
406+ struct ref_entry * entry =
407+ create_ref_entry (iter -> refname , iter -> oid , iter -> flags );
277408
278- eol = memchr (p , '\n' , eof - p );
279- if (!eol )
280- die_unterminated_line (refs -> path , pos , eof - pos );
281-
282- strbuf_add (& tmp , p , eol - p );
283- refname = tmp .buf ;
284-
285- if (check_refname_format (refname , REFNAME_ALLOW_ONELEVEL )) {
286- if (!refname_is_safe (refname ))
287- die ("packed refname is dangerous: %s" , refname );
288- oidclr (& oid );
289- flag |= REF_BAD_NAME | REF_ISBROKEN ;
290- }
291- if (packed_refs -> peeled == PEELED_FULLY ||
292- (packed_refs -> peeled == PEELED_TAGS &&
293- starts_with (refname , "refs/tags/" )))
294- flag |= REF_KNOWS_PEELED ;
295- entry = create_ref_entry (refname , & oid , flag );
409+ if ((iter -> flags & REF_KNOWS_PEELED ))
410+ ref_iterator_peel (iter , & entry -> u .value .peeled );
296411 add_ref_entry (dir , entry );
297-
298- pos = eol + 1 ;
299-
300- if (pos < eof && * pos == '^' ) {
301- p = pos + 1 ;
302- if (eof - p < GIT_SHA1_HEXSZ + 1 ||
303- parse_oid_hex (p , & entry -> u .value .peeled , & p ) ||
304- * p ++ != '\n' )
305- die_invalid_line (refs -> path , pos , eof - pos );
306-
307- /*
308- * Regardless of what the file header said,
309- * we definitely know the value of *this*
310- * reference:
311- */
312- entry -> flag |= REF_KNOWS_PEELED ;
313-
314- pos = p ;
315- }
316-
317- strbuf_reset (& tmp );
318412 }
319413
414+ if (ok != ITER_DONE )
415+ die ("error reading packed-refs file %s" , refs -> path );
416+
320417 if (munmap (buf , size ))
321- die_errno ("error ummapping packed-refs file" );
418+ die_errno ("error ummapping packed-refs file %s" , refs -> path );
419+
322420 close (fd );
323421
324- strbuf_release (& tmp );
325422 return packed_refs ;
326423}
327424
0 commit comments