From: Taylor Blau <hidden> Date: 2021-01-14 02:06:31
Remove direct manipulation of the 'struct revindex_entry' type as well
as calls to the deprecated API in 'packfile.c:unpack_entry()'. Usual
clean-up is performed (replacing '->nr' with calls to
'pack_pos_to_index()' and so on).
Add an additional check to make sure that 'obj_offset()' points at a
valid object. In the case this check is violated, we cannot call
'mark_bad_packed_object()' because we don't know the OID. At the top of
the call stack is do_oid_object_info_extended() (via
packed_object_info()), which does mark the object.
Signed-off-by: Taylor Blau <redacted>
---
packfile.c | 26 ++++++++++++++++++--------
1 file changed, 18 insertions(+), 8 deletions(-)
From: Taylor Blau <hidden> Date: 2021-01-14 02:07:19
Now that all 'find_revindex_position()' callers have been removed (and
converted to the more descriptive 'offset_to_pack_pos()'), it is almost
safe to get rid of 'find_revindex_position()' entirely. Almost, except
for the fact that 'offset_to_pack_pos()' calls
'find_revindex_position()'.
Inline 'find_revindex_position()' into 'offset_to_pack_pos()', and
then remove 'find_revindex_position()' entirely.
This is a straightforward refactoring with one minor snag.
'offset_to_pack_pos()' used to load the index before calling
'find_revindex_position()'. That means that by the time
'find_revindex_position()' starts executing, 'p->num_objects' can be
safely read. After inlining, be careful to not read 'p->num_objects'
until _after_ 'load_pack_revindex()' (which loads the index as a
side-effect) has been called.
Another small fix that is included is converting the upper- and
lower-bounds to be unsigned's instead of ints. This dates back to
92e5c77c37 (revindex: export new APIs, 2013-10-24)--ironically, the last
time we introduced new APIs here--but this unifies the types.
Signed-off-by: Taylor Blau <redacted>
---
pack-revindex.c | 31 ++++++++++++-------------------
pack-revindex.h | 1 -
2 files changed, 12 insertions(+), 20 deletions(-)
From: Taylor Blau <hidden> Date: 2021-01-14 02:09:55
Remove another caller that holds onto a 'struct revindex_entry' by
replacing the direct indexing with calls to 'pack_pos_to_offset()' and
'pack_pos_to_index()'.
Signed-off-by: Taylor Blau <redacted>
---
pack-bitmap.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
@@ -835,11 +835,11 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,oi.sizep=&size;if(pos<pack->num_objects){-structrevindex_entry*entry=&pack->revindex[pos];-if(packed_object_info(the_repository,pack,-entry->offset,&oi)<0){+off_tofs=pack_pos_to_offset(pack,pos);+if(packed_object_info(the_repository,pack,ofs,&oi)<0){structobject_idoid;-nth_packed_object_id(&oid,pack,entry->nr);+nth_packed_object_id(&oid,pack,+pack_pos_to_index(pack,pos));die(_("unable to get size of %s"),oid_to_hex(&oid));}}else{
From: Taylor Blau <hidden> Date: 2021-01-14 02:09:55
Replace a direct access to the revindex array with
'pack_pos_to_offset()'.
Signed-off-by: Taylor Blau <redacted>
---
builtin/pack-objects.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Taylor Blau <hidden> Date: 2021-01-14 02:09:55
Replace find_revindex_position() with its counterpart in the new API,
offset_to_pack_pos().
Signed-off-by: Taylor Blau <redacted>
---
pack-bitmap.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
From: Taylor Blau <hidden> Date: 2021-01-14 02:10:47
First replace 'find_pack_revindex()' with its replacement
'offset_to_pack_pos()'. This prevents any bogus OFS_DELTA that may make
its way through until 'write_reuse_object()' from causing a bad memory
read (if 'revidx' is 'NULL')
Next, replace a direct access of '->nr' with the wrapper function
'pack_pos_to_index()'.
Signed-off-by: Taylor Blau <redacted>
---
builtin/pack-objects.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
From: Taylor Blau <hidden> Date: 2021-01-14 02:12:20
Replace direct accesses to the revindex with calls to
'offset_to_pack_pos()' and 'pack_pos_to_index()'.
Since this caller already had some error checking (it can jump to the
'give_up' label if it encounters an error), we can easily check whether
or not the provided offset points to an object in the given pack. This
error checking existed prior to this patch, too, since the caller checks
whether the return value from 'find_pack_revindex()' was NULL or not.
Signed-off-by: Taylor Blau <redacted>
---
builtin/pack-objects.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
From: Taylor Blau <hidden> Date: 2021-01-14 02:12:20
Hi,
Here is a revision of the first of two series to prepare for and introduce an
on-disk alternative for storing the reverse index.
In this revision, I addressed feedback from Junio, Peff, and Stolee. A
range-diff is included below, but the main changes are:
- Error messages are improved to include the pack and offset when applicable.
- Variable names were made clearer (e.g., n -> index_pos).
- Comments were added in pack-revindex.h to introduce relevant terminology,
and which methods convert between what orderings.
- int-sized lower- and upper-bounds were converted to be unsigned.
I believe that this revision should be ready for queueing. I'll send a v2 of the
corresponding latter series shortly.
Thanks in advance for your review.
Taylor Blau (20):
pack-revindex: introduce a new API
write_reuse_object(): convert to new revindex API
write_reused_pack_one(): convert to new revindex API
write_reused_pack_verbatim(): convert to new revindex API
check_object(): convert to new revindex API
bitmap_position_packfile(): convert to new revindex API
show_objects_for_type(): convert to new revindex API
get_size_by_pos(): convert to new revindex API
try_partial_reuse(): convert to new revindex API
rebuild_existing_bitmaps(): convert to new revindex API
get_delta_base_oid(): convert to new revindex API
retry_bad_packed_offset(): convert to new revindex API
packed_object_info(): convert to new revindex API
unpack_entry(): convert to new revindex API
for_each_object_in_pack(): convert to new revindex API
builtin/gc.c: guess the size of the revindex
pack-revindex: remove unused 'find_pack_revindex()'
pack-revindex: remove unused 'find_revindex_position()'
pack-revindex: hide the definition of 'revindex_entry'
pack-revindex.c: avoid direct revindex access in
'offset_to_pack_pos()'
builtin/gc.c | 2 +-
builtin/pack-objects.c | 37 +++++++++++++++++---------
pack-bitmap.c | 44 +++++++++++++++----------------
pack-revindex.c | 51 ++++++++++++++++++++++-------------
pack-revindex.h | 60 +++++++++++++++++++++++++++++++++++++-----
packfile.c | 54 ++++++++++++++++++++++++-------------
6 files changed, 168 insertions(+), 80 deletions(-)
Range-diff against v1:
1: fa6b830908 < -: ---------- pack-revindex: introduce a new API
-: ---------- > 1: e1aa89244a pack-revindex: introduce a new API
2: 00668523e1 ! 2: 0fca7d5812 write_reuse_object(): convert to new revindex API
@@ builtin/pack-objects.c: static off_t write_reuse_object(struct hashfile *f, stru
- revidx = find_pack_revindex(p, offset);
- datalen = revidx[1].offset - offset;
+ if (offset_to_pack_pos(p, offset, &pos) < 0)
-+ die(_("write_reuse_object: could not locate %s"),
-+ oid_to_hex(&entry->idx.oid));
++ die(_("write_reuse_object: could not locate %s, expected at "
++ "offset %"PRIuMAX" in pack %s"),
++ oid_to_hex(&entry->idx.oid), (uintmax_t)offset,
++ p->pack_name);
+ datalen = pack_pos_to_offset(p, pos + 1) - offset;
if (!pack_to_stdout && p->index_version > 1 &&
- check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
3: 81ab11e18c ! 3: 7676822a54 write_reused_pack_one(): convert to new revindex API
@@ builtin/pack-objects.c: static void write_reused_pack_one(size_t pos, struct has
struct object_id base_oid;
+ if (offset_to_pack_pos(reuse_packfile, base_offset, &base_pos) < 0)
-+ die(_("expected object at offset %"PRIuMAX),
-+ (uintmax_t)base_offset);
++ die(_("expected object at offset %"PRIuMAX" "
++ "in pack %s"),
++ (uintmax_t)base_offset,
++ reuse_packfile->pack_name);
+
nth_packed_object_id(&base_oid, reuse_packfile,
- reuse_packfile->revindex[base_pos].nr);
4: 14b35d01a0 = 4: dd7133fdb7 write_reused_pack_verbatim(): convert to new revindex API
5: c47e77a30e = 5: 8e93ca3886 check_object(): convert to new revindex API
6: 3b170663dd = 6: 084bbf2145 bitmap_position_packfile(): convert to new revindex API
7: bc67bb462a ! 7: 68794e9484 show_objects_for_type(): convert to new revindex API
@@ pack-bitmap.c: static void show_objects_for_type(
struct object_id oid;
- struct revindex_entry *entry;
- uint32_t hash = 0;
-+ uint32_t hash = 0, n;
++ uint32_t hash = 0, index_pos;
+ off_t ofs;
if ((word >> offset) == 0)
@@ pack-bitmap.c: static void show_objects_for_type(
- entry = &bitmap_git->pack->revindex[pos + offset];
- nth_packed_object_id(&oid, bitmap_git->pack, entry->nr);
-+ n = pack_pos_to_index(bitmap_git->pack, pos + offset);
++ index_pos = pack_pos_to_index(bitmap_git->pack, pos + offset);
+ ofs = pack_pos_to_offset(bitmap_git->pack, pos + offset);
-+ nth_packed_object_id(&oid, bitmap_git->pack, n);
++ nth_packed_object_id(&oid, bitmap_git->pack, index_pos);
if (bitmap_git->hashes)
- hash = get_be32(bitmap_git->hashes + entry->nr);
-+ hash = get_be32(bitmap_git->hashes + n);
++ hash = get_be32(bitmap_git->hashes + index_pos);
- show_reach(&oid, object_type, 0, hash, bitmap_git->pack, entry->offset);
+ show_reach(&oid, object_type, 0, hash, bitmap_git->pack, ofs);
8: 541fe679f3 = 8: 31ac6f5703 get_size_by_pos(): convert to new revindex API
9: 54f4ad329f ! 9: acd80069a2 try_partial_reuse(): convert to new revindex API
@@ Commit message
'pack_pos_to_offset()' instead (the caller here does not care about the
index position of the object at position 'pos').
- Somewhat confusingly, the subsequent call to unpack_object_header()
- takes a pointer to &offset and then updates it with a new value. But,
- try_partial_reuse() cares about the offset of both the base's header and
- contents. The existing code made a copy of the offset field, and only
- addresses and manipulates one of them.
-
- Instead, store the return of pack_pos_to_offset twice: once in header
- and another in offset. Header will be left untouched, but offset will be
- addressed and modified by unpack_object_header().
+ Note that we cannot just use the existing "offset" variable to store the
+ value we get from pack_pos_to_offset(). It is incremented by
+ unpack_object_header(), but we later need the original value. Since
+ we'll no longer have revindex->offset to read it from, we'll store that
+ in a separate variable ("header" since it points to the entry's header
+ bytes).
Signed-off-by: Taylor Blau [off-list ref]
10: 97eaa7b2d6 = 10: 569acdca7f rebuild_existing_bitmaps(): convert to new revindex API
11: e00c434ab2 = 11: 9881637724 get_delta_base_oid(): convert to new revindex API
12: aae01d7029 = 12: df8bb571a5 retry_bad_packed_offset(): convert to new revindex API
13: eab7ab1f35 ! 13: 41b2e00947 packed_object_info(): convert to new revindex API
@@ packfile.c: int packed_object_info(struct repository *r, struct packed_git *p,
- *oi->disk_sizep = revidx[1].offset - obj_offset;
+ uint32_t pos;
+ if (offset_to_pack_pos(p, obj_offset, &pos) < 0) {
++ error("could not find object at offset %"PRIuMAX" "
++ "in pack %s", (uintmax_t)obj_offset, p->pack_name);
+ type = OBJ_BAD;
+ goto out;
+ }
14: 13c49ed40c ! 14: 8ad49d231f unpack_entry(): convert to new revindex API
@@ Commit message
Remove direct manipulation of the 'struct revindex_entry' type as well
as calls to the deprecated API in 'packfile.c:unpack_entry()'. Usual
clean-up is performed (replacing '->nr' with calls to
- 'pack_pos_to_index()' and so on). Add an additional check to make
- sure that 'obj_offset()' points at a valid object.
+ 'pack_pos_to_index()' and so on).
+
+ Add an additional check to make sure that 'obj_offset()' points at a
+ valid object. In the case this check is violated, we cannot call
+ 'mark_bad_packed_object()' because we don't know the OID. At the top of
+ the call stack is do_oid_object_info_extended() (via
+ packed_object_info()), which does mark the object.
Signed-off-by: Taylor Blau [off-list ref]
@@ packfile.c: void *unpack_entry(struct repository *r, struct packed_git *p, off_t
- struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
- off_t len = revidx[1].offset - obj_offset;
- if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
-+ uint32_t pos, nr;
++ uint32_t pack_pos, index_pos;
+ off_t len;
+
-+ if (offset_to_pack_pos(p, obj_offset, &pos) < 0) {
++ if (offset_to_pack_pos(p, obj_offset, &pack_pos) < 0) {
++ error("could not find object at offset %"PRIuMAX" in pack %s",
++ (uintmax_t)obj_offset, p->pack_name);
+ data = NULL;
+ goto out;
+ }
+
-+ len = pack_pos_to_offset(p, pos + 1) - obj_offset;
-+ nr = pack_pos_to_index(p, pos);
-+ if (check_pack_crc(p, &w_curs, obj_offset, len, nr)) {
++ len = pack_pos_to_offset(p, pack_pos + 1) - obj_offset;
++ index_pos = pack_pos_to_index(p, pack_pos);
++ if (check_pack_crc(p, &w_curs, obj_offset, len, index_pos)) {
struct object_id oid;
- nth_packed_object_id(&oid, p, revidx->nr);
-+ nth_packed_object_id(&oid, p, nr);
++ nth_packed_object_id(&oid, p, index_pos);
error("bad packed object CRC for %s",
oid_to_hex(&oid));
mark_bad_packed_object(p, oid.hash);
15: a3249986f9 = 15: e757476351 for_each_object_in_pack(): convert to new revindex API
16: 7c17db7a7d = 16: a500311e33 builtin/gc.c: guess the size of the revindex
17: c4c88bcc3d ! 17: 67d14da04a pack-revindex: remove unused 'find_pack_revindex()'
@@ pack-revindex.h: struct revindex_entry {
-struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs);
-
- int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos);
- uint32_t pack_pos_to_index(struct packed_git *p, uint32_t pos);
- off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos);
+ /*
+ * offset_to_pack_pos converts an object offset to a pack position. This
+ * function returns zero on success, and a negative number otherwise. The
18: d60411d524 ! 18: 3b5c92be68 pack-revindex: remove unused 'find_revindex_position()'
@@ Commit message
until _after_ 'load_pack_revindex()' (which loads the index as a
side-effect) has been called.
+ Another small fix that is included is converting the upper- and
+ lower-bounds to be unsigned's instead of ints. This dates back to
+ 92e5c77c37 (revindex: export new APIs, 2013-10-24)--ironically, the last
+ time we introduced new APIs here--but this unifies the types.
+
Signed-off-by: Taylor Blau [off-list ref]
## pack-revindex.c ##
@@ pack-revindex.c: int load_pack_revindex(struct packed_git *p)
-int find_revindex_position(struct packed_git *p, off_t ofs)
+int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos)
{
- int lo = 0;
+- int lo = 0;
- int hi = p->num_objects + 1;
- const struct revindex_entry *revindex = p->revindex;
-+ int hi;
++ unsigned lo, hi;
+ const struct revindex_entry *revindex;
+
+ if (load_pack_revindex(p) < 0)
+ return -1;
+
++ lo = 0;
+ hi = p->num_objects + 1;
+ revindex = p->revindex;
@@ pack-revindex.c: int find_revindex_position(struct packed_git *p, off_t ofs)
-
- ret = find_revindex_position(p, ofs);
- if (ret < 0)
-- return -1;
+- return ret;
- *pos = ret;
- return 0;
-}
@@ pack-revindex.c: int find_revindex_position(struct packed_git *p, off_t ofs)
## pack-revindex.h ##
@@ pack-revindex.h: struct revindex_entry {
- };
-
+ * given pack, returning zero on success and a negative value otherwise.
+ */
int load_pack_revindex(struct packed_git *p);
-int find_revindex_position(struct packed_git *p, off_t ofs);
- int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos);
- uint32_t pack_pos_to_index(struct packed_git *p, uint32_t pos);
+ /*
+ * offset_to_pack_pos converts an object offset to a pack position. This
19: 7c0e4acc84 ! 19: cabafce4a1 pack-revindex: hide the definition of 'revindex_entry'
@@ pack-revindex.h
- unsigned int nr;
-};
-
- int load_pack_revindex(struct packed_git *p);
-
- int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos);
+ /*
+ * load_pack_revindex populates the revindex's internal data-structures for the
+ * given pack, returning zero on success and a negative value otherwise.
20: eada1ffcfa ! 20: 8400ff6c96 pack-revindex.c: avoid direct revindex access in 'offset_to_pack_pos()'
@@ Commit message
Signed-off-by: Taylor Blau [off-list ref]
## pack-revindex.c ##
-@@ pack-revindex.c: int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos)
+@@ pack-revindex.c: int load_pack_revindex(struct packed_git *p)
+ int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos)
{
- int lo = 0;
- int hi;
+ unsigned lo, hi;
- const struct revindex_entry *revindex;
if (load_pack_revindex(p) < 0)
return -1;
+ lo = 0;
hi = p->num_objects + 1;
- revindex = p->revindex;
--
2.30.0.138.g6d7191ea01
From: Junio C Hamano <hidden> Date: 2021-01-14 06:43:36
Taylor Blau [off-list ref] writes:
quoted hunk
-int find_revindex_position(struct packed_git *p, off_t ofs)+int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos) {- int lo = 0;- int hi = p->num_objects + 1;- const struct revindex_entry *revindex = p->revindex;+ unsigned lo, hi;+ const struct revindex_entry *revindex;++ if (load_pack_revindex(p) < 0)+ return -1;++ lo = 0;+ hi = p->num_objects + 1;+ revindex = p->revindex; do { const unsigned mi = lo + (hi - lo) / 2; if (revindex[mi].offset == ofs) {- return mi;+ *pos = mi;+ return 0; } else if (ofs < revindex[mi].offset) hi = mi; else
OK, we can safely depend on "unsigned int" at least as wide as
"uint32_t"; unlike the original that used "int", we won't risk
losing the upper half of 4G range.
Nice.
@@ -2086,7 +2086,7 @@ int for_each_object_in_pack(struct packed_git *p,structobject_idoid;if(flags&FOR_EACH_OBJECT_PACK_ORDER)-pos=p->revindex[i].nr;+pos=pack_pos_to_index(p,i);
It wasn't too bad before this series formally defined what
"position", "index" and "offset" mean, but now this has become
highly misleading. The variable "pos" here holds what we consider
"index" while "i" holds what we call "position" [*1*].
else
pos = i;
Perhaps renaming "uint32_t pos" to "nth" would avoid confusion?
- if (nth_packed_object_id(&oid, p, pos) < 0)
+ if (nth_packed_object_id(&oid, p, nth) < 0)
return error(...);
[Footnote]
*1* The nth_packed_object_id() call we make later using the value we
obtain here should be documented to take "index" as its last
parameter, now that is what we call the location in the index, which
is in object name order.
From: Junio C Hamano <hidden> Date: 2021-01-14 06:47:43
Taylor Blau [off-list ref] writes:
quoted hunk
+/*+ * offset_to_pack_pos converts an object offset to a pack position. This+ * function returns zero on success, and a negative number otherwise. The+ * parameter 'pos' is usable only on success.+ *+ * If the reverse index has not yet been loaded, this function loads it lazily,+ * and returns an negative number if an error was encountered.
It is somewhat strange to see a function that yields a non-negative
"position" on success and a negative value to signal a failure to
have a separate pointer to the location to receive the true return
value. Do we truly care the upper half of "uint32_t" (in other
words, do we seriously want to support more than 2G positions in a
pack)?
What I'm trying to get at is that
int pos = offset_to_pack_pos(...);
if (pos < 0)
error();
else
use(pos);
is more natural than
uint32_t pos;
if (offset_to_pack_pos(..., &pos) < 0)
error();
else
use(pos);
but now I wrote it down and laid it out in front of my eyes, the
latter does not look too bad.
... later comes back after reading through the series ...
The new callers all looked quite nice to eyes. Because we
discourage assignment inside if() condition, the converted
result does not make the code more verbose than the
original. In fact, it makes it even clearer that we are
checking for an error return from a function call.
Quite nice.
+ * This function runs in time O(log N) with the number of objects in the pack.
Is it a good idea to commit to such performance characteristics as a
promise to callers like this (the comment applies to all three
functions)?
It depends on how a developer is helped by this comment when
deciding whether to use this function, or find other ways, to
implement what s/he wants to do.
+/*+ * pack_pos_to_index converts the given pack-relative position 'pos' by+ * returning an index-relative position.+ *+ * If the reverse index has not yet been loaded, or the position is out of+ * bounds, this function aborts.+ *+ * This function runs in constant time.+ */+uint32_t pack_pos_to_index(struct packed_git *p, uint32_t pos);++/*+ * pack_pos_to_offset converts the given pack-relative position 'pos' into a+ * pack offset. For a pack with 'N' objects, asking for position 'N' will return+ * the total size (in bytes) of the pack.
If we talk about "asking for 'N'" and want it to mean "one beyond
the last position", it is better to clarify that we count from 0.
But see below.
+ * If the reverse index has not yet been loaded, or the position is out of
+ * bounds, this function aborts.
I think it is easier to read if the "unlike the above function, this
allows pos that is one beyond the last object" is explained next to
"if out of bounds, it is an error", not as a part of the previous
paragraph.
quoted hunk
+ * This function runs in constant time.+ */+off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos);+ #endif
+/*+ * offset_to_pack_pos converts an object offset to a pack position. This+ * function returns zero on success, and a negative number otherwise. The+ * parameter 'pos' is usable only on success.+ *+ * If the reverse index has not yet been loaded, this function loads it lazily,+ * and returns an negative number if an error was encountered.
It is somewhat strange to see a function that yields a non-negative
"position" on success and a negative value to signal a failure to
have a separate pointer to the location to receive the true return
value. Do we truly care the upper half of "uint32_t" (in other
words, do we seriously want to support more than 2G positions in a
pack)?
What I'm trying to get at is that
int pos = offset_to_pack_pos(...);
if (pos < 0)
error();
else
use(pos);
is more natural than
I agree that this is used commonly, but usually in the case that
we are finding a position in the list _or where such an item would
be inserted_. For example:
pos = index_name_pos(istate, dirname, len);
if (pos < 0)
pos = -pos-1;
while (pos < istate->cache_nr) {
...
But that does not apply in this case. Knowing that the requested
offset lies between object 'i' and object 'i + 1' isn't helpful,
since the offset still does not correspond to the start of an
object.
uint32_t pos;
if (offset_to_pack_pos(..., &pos) < 0)
error();
else
use(pos);
but now I wrote it down and laid it out in front of my eyes, the
latter does not look too bad.
... later comes back after reading through the series ...
The new callers all looked quite nice to eyes. Because we
discourage assignment inside if() condition, the converted
result does not make the code more verbose than the
original. In fact, it makes it even clearer that we are
checking for an error return from a function call.
Quite nice.
As someone who spends a decent amount of time working in C#, I
also like this pattern. The APIs in C# work this way, too, such
as:
if (!set.TryGetValue(key, out value))
return false;
// Use 'value', which is initialized now.
Thanks,
-Stolee
@@ -2086,7 +2086,7 @@ int for_each_object_in_pack(struct packed_git *p,structobject_idoid;if(flags&FOR_EACH_OBJECT_PACK_ORDER)-pos=p->revindex[i].nr;+pos=pack_pos_to_index(p,i);
It wasn't too bad before this series formally defined what
"position", "index" and "offset" mean, but now this has become
highly misleading. The variable "pos" here holds what we consider
"index" while "i" holds what we call "position" [*1*].
quoted
else
pos = i;
Perhaps renaming "uint32_t pos" to "nth" would avoid confusion?
I agree that it can be confusing. Unfortunately in this spot, this
variable really does mean two things. If we set the
FOR_EACH_OBJECT_PACK_ORDER bit in our flags, then the caller really
wants the index position (and the objects to be delivered in pack
order). But if we didn't set it, then the caller wants it in index
order.
- if (nth_packed_object_id(&oid, p, pos) < 0)
+ if (nth_packed_object_id(&oid, p, nth) < 0)
return error(...);
This suggested diff makes me think that you understand all of that, so
I'm mostly saying this for the benefit of others that haven't looked at
this code closely in the recent past.
I'd be happy to send a replacement patch if you would like [1], but I'm
hopeful that this is clear enough since there isn't much code between
the declaration, assignment(s), and use of 'pos'.
Thanks,
Taylor
[1]: I understand your general disdain for single replacement patches,
but I'd like to avoid sending the other 19 patches if possible to avoid
delivering more mail to list subscribers than is necessary.
From: Taylor Blau <hidden> Date: 2021-01-14 17:07:13
On Wed, Jan 13, 2021 at 10:46:57PM -0800, Junio C Hamano wrote:
Taylor Blau [off-list ref] writes:
quoted
+/*+ * offset_to_pack_pos converts an object offset to a pack position. This+ * function returns zero on success, and a negative number otherwise. The+ * parameter 'pos' is usable only on success.+ *+ * If the reverse index has not yet been loaded, this function loads it lazily,+ * and returns an negative number if an error was encountered.
It is somewhat strange to see a function that yields a non-negative
"position" on success and a negative value to signal a failure to
have a separate pointer to the location to receive the true return
value. Do we truly care the upper half of "uint32_t" (in other
words, do we seriously want to support more than 2G positions in a
pack)?
I don't think that we care about that as much as we do about potential
misuse of a signed return value. There are indeed a couple of spots
where a potential negative return value is ignored, and then used to
lookup an object in a pack, or some such.
And that's part of the goal of this API: we have strict guidelines about
when the output parameter is and isn't usable. That makes it more
difficult to accidentally use an uninitialized value / negative number.
What I'm trying to get at is that [...] is more natural than [...] but
now I wrote it down and laid it out in front of my eyes, the latter
does not look too bad.
OK, good :-).
... later comes back after reading through the series ...
The new callers all looked quite nice to eyes. Because we
discourage assignment inside if() condition, the converted
result does not make the code more verbose than the
original. In fact, it makes it even clearer that we are
checking for an error return from a function call.
Quite nice.
Thank you :-D.
quoted
+ * This function runs in time O(log N) with the number of objects in the pack.
Is it a good idea to commit to such performance characteristics as a
promise to callers like this (the comment applies to all three
functions)?
It depends on how a developer is helped by this comment when
deciding whether to use this function, or find other ways, to
implement what s/he wants to do.
From: Jeff King <hidden> Date: 2021-01-14 19:20:01
On Thu, Jan 14, 2021 at 12:06:20PM -0500, Taylor Blau wrote:
quoted
quoted
+ * This function runs in time O(log N) with the number of objects in the pack.
Is it a good idea to commit to such performance characteristics as a
promise to callers like this (the comment applies to all three
functions)?
It depends on how a developer is helped by this comment when
deciding whether to use this function, or find other ways, to
implement what s/he wants to do.
I don't mind it. If they all had the same performance characteristics, I
wouldn't be for it, but since they don't, I think that it's good to
know. Peff suggested this back in [1].
Yeah, I asked for this. As somebody who has frequently worked on the
code which accesses the revindex (mostly bitmap stuff), I found it
useful to understand how expensive the operations were. However, I also
know what their runtimes are at this point, and it is not like somebody
interested cannot look at the implementation. So it may not be that
important.
So I do still think it is useful, but if somebody feels strongly against
it, I don't mind it being removed.
-Peff
It wasn't too bad before this series formally defined what
"position", "index" and "offset" mean, but now this has become
highly misleading. The variable "pos" here holds what we consider
"index" while "i" holds what we call "position" [*1*].
I don't think "position" is a meaningful term by itself. I would say the
useful terms are "pack position", "index position", and "offset" (or
"pack offset" if you like). I don't think anything in the definitions
added by earlier patches contradicts that, but perhaps we can make it
more clear.
So "pos" in this case is not wrong. But I agree that it could stand to
be more clear. Saying "nth" does not help things IMHO (there is an "nth"
pack position, as well).
But maybe this makes it more clear (or possibly just the name change
without the comment):
@@ -2078,19 +2078,30 @@ int for_each_object_in_pack(struct packed_git *p,}for(i=0;i<p->num_objects;i++){-uint32_tpos;+uint32_tindex_pos;structobject_idoid;+/*+*Weareiterating"i"from0uptonum_objects,butits+*meaningmaybedifferent:+*+*-inobject-nameorder,itisthesameastheindexorder+*giventousbynth_packed_object_id(),andwecanuseit+*directly+*+*-inpack-order,itispackposition,whichwemust+*converttoanindexpositioninordertogettheoid.+*/if(flags&FOR_EACH_OBJECT_PACK_ORDER)-pos=p->revindex[i].nr;+index_pos=p->revindex[i].nr;else-pos=i;+index_pos=i;-if(nth_packed_object_id(&oid,p,pos)<0)+if(nth_packed_object_id(&oid,p,index_pos)<0)returnerror("unable to get sha1 of object %u in %s",-pos,p->pack_name);+index_pos,p->pack_name);-r=cb(&oid,p,pos,data);+r=cb(&oid,p,index_pos,data);if(r)break;}
*1* The nth_packed_object_id() call we make later using the value we
obtain here should be documented to take "index" as its last
parameter, now that is what we call the location in the index, which
is in object name order.
I would love to see the function given a more descriptive name. Having
worked on the bitmap code a lot, where the norm is pack-order, saying
"nth" is confusing and error-prone.
But I think that's out of scope for this series.
-Peff
From: Jeff King <hidden> Date: 2021-01-14 19:52:20
On Wed, Jan 13, 2021 at 05:23:25PM -0500, Taylor Blau wrote:
In this revision, I addressed feedback from Junio, Peff, and Stolee. A
range-diff is included below, but the main changes are:
- Error messages are improved to include the pack and offset when applicable.
- Variable names were made clearer (e.g., n -> index_pos).
- Comments were added in pack-revindex.h to introduce relevant terminology,
and which methods convert between what orderings.
- int-sized lower- and upper-bounds were converted to be unsigned.
Thanks, this addresses all of my nits. I responded to a few of Junio's
reviews with some further comments/suggestions; the most interesting one
is using "index_pos" to indicate the ordering more clearly in patch 15.
I'm happy with or without including that.
-Peff
From: Jeff King <hidden> Date: 2021-01-14 20:11:52
On Thu, Jan 14, 2021 at 02:33:53PM -0500, Jeff King wrote:
So "pos" in this case is not wrong. But I agree that it could stand to
be more clear. Saying "nth" does not help things IMHO (there is an "nth"
pack position, as well).
But maybe this makes it more clear (or possibly just the name change
without the comment):
Here it is again, but with a signoff and commit message, and done on top
of your series (so if we agree this is a good resolution, it can just be
picked up on top, but I am also happy for it to be squashed into patch
15).
-- >8 --
Subject: [PATCH] for_each_object_in_pack(): clarify pack vs index ordering
We may return objects in one of two orders: how they appear in the .idx
(sorted by object id) or how they appear in the packfile itself. To
further complicate matters, we have two ordering variables, "i" and
"pos", and it is not clear to which order they apply.
Let's clarify this by using an unambiguous name where possible, and
leaving a comment for the variable that does double-duty.
Signed-off-by: Jeff King <redacted>
---
packfile.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
@@ -2082,19 +2082,31 @@ int for_each_object_in_pack(struct packed_git *p,}for(i=0;i<p->num_objects;i++){-uint32_tpos;+uint32_tindex_pos;structobject_idoid;+/*+*Weareiterating"i"from0uptonum_objects,butits+*meaningmaybedifferent,dependingontherequestedoutput+*order:+*+*-inobject-nameorder,itisthesameastheindexorder+*usedbynth_packed_object_id(),sowecanpassit+*directly+*+*-inpack-order,itispackposition,whichwemust+*converttoanindexpositioninordertogettheoid.+*/if(flags&FOR_EACH_OBJECT_PACK_ORDER)-pos=pack_pos_to_index(p,i);+index_pos=pack_pos_to_index(p,i);else-pos=i;+index_pos=i;-if(nth_packed_object_id(&oid,p,pos)<0)+if(nth_packed_object_id(&oid,p,index_pos)<0)returnerror("unable to get sha1 of object %u in %s",-pos,p->pack_name);+index_pos,p->pack_name);-r=cb(&oid,p,pos,data);+r=cb(&oid,p,index_pos,data);if(r)break;}
From: Taylor Blau <hidden> Date: 2021-01-14 20:16:44
On Thu, Jan 14, 2021 at 03:11:10PM -0500, Jeff King wrote:
On Thu, Jan 14, 2021 at 02:33:53PM -0500, Jeff King wrote:
quoted
So "pos" in this case is not wrong. But I agree that it could stand to
be more clear. Saying "nth" does not help things IMHO (there is an "nth"
pack position, as well).
But maybe this makes it more clear (or possibly just the name change
without the comment):
Here it is again, but with a signoff and commit message, and done on top
of your series (so if we agree this is a good resolution, it can just be
picked up on top, but I am also happy for it to be squashed into patch
15).
Much appreciated. This looks good to me (and I have no opinion whether
it is picked up on top, or squashed into patch 15).
Acked-by: Taylor Blau [off-list ref]
Thanks,
Taylor