mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 17:27:53 +03:00
2c9fffd80e
Summary: The current linelog APIs are designed purely for the "annotate" operation. It only supports getting lines from one single revisions, which is what `annotate` does. However the data structure is also useful to provide lines from different revisions. Say if you want to know all lines from all revisions for a given function. Or if you want to confirm if a chunk is "continuous" - nobody inserts new lines among them. This diff adds the missing API, `getalllines`. It returns information about all lines the linelog tracks. The caller could provide an optional interval to limit the lines returned. Its parameters use low-level raw offsets instead of line numbers, because it gives the caller more control. The API don't need to handle open or close interval issues that line numbers may have (see the previous diff about deletion blocks). This makes `lineinfo.offset` no longer internal-only - we have exposed the offset concept to the API parameters. Therefore the comments are updated. This also requires a clear way to distinguish unconditional jumps from conditional one. We use `JGE 0` as unconditional jumps. To ensure this, make `replacelines` reject 0 revision. Test Plan: `cd linelog && make` Reviewers: #mercurial, ttung, simonfar Reviewed By: simonfar Subscribers: mjpieters Differential Revision: https://phabricator.intern.facebook.com/D3716439 Signature: t1:3716439:1471265245:5a4b2f585284cba71ca3907c8e8826b2b00a4b49
158 lines
6.2 KiB
C
158 lines
6.2 KiB
C
#ifndef LINELOG_H_ZUJREV4L
|
|
#define LINELOG_H_ZUJREV4L
|
|
|
|
/*
|
|
* Copyright 2016-present Facebook. All Rights Reserved.
|
|
*
|
|
* linelog.h: data structure tracking line changes
|
|
*
|
|
* This software may be used and distributed according to the terms of the
|
|
* GNU General Public License version 2 or any later version.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
/* static assert sizeof(size_t) >= sizeof(uint32_t) */
|
|
extern char linelog_assert_sizet_[1 / (sizeof(size_t) >= 4)];
|
|
|
|
typedef uint32_t linelog_linenum; /* line number, starting from 0 */
|
|
typedef uint32_t linelog_revnum; /* rev x is the only parent of rev x + 1 */
|
|
typedef uint32_t linelog_offset; /* index of linelog_buf.data */
|
|
|
|
typedef int linelog_result; /* return value of some apis */
|
|
|
|
#define LINELOG_RESULT_OK (0) /* success */
|
|
#define LINELOG_RESULT_ENOMEM (-1) /* failed to malloc or realloc */
|
|
#define LINELOG_RESULT_EILLDATA (-2) /* illegal data, unexpected values */
|
|
#define LINELOG_RESULT_EOVERFLOW (-3) /* hard limit exceeded */
|
|
#define LINELOG_RESULT_ENEEDRESIZE (-4) /* buf.size should >= neededsize */
|
|
|
|
/* main storage (memory buffer) for linelog data, allocated by caller
|
|
same on-disk and in-memory format, endianness-insensitive.
|
|
designed to be used with mmap for efficient updates. */
|
|
typedef struct {
|
|
uint8_t *data; /* mmap-friendly, set by caller */
|
|
size_t size; /* bytes, set by caller */
|
|
size_t neededsize; /* set by callee on ENEEDRESIZE */
|
|
} linelog_buf;
|
|
|
|
/* an annotated line */
|
|
typedef struct {
|
|
linelog_revnum rev; /* revision number at the first appearance */
|
|
linelog_linenum linenum; /* line number at the first appearance */
|
|
linelog_offset offset; /* index of linelog_buf.data */
|
|
} linelog_lineinfo;
|
|
|
|
/* annotate result, an dynamic array of linelog_lineinfo, allocated by callee
|
|
memset to 0 before use, call linelog_annotateresult_clear to free memory */
|
|
typedef struct {
|
|
linelog_lineinfo *lines;
|
|
linelog_linenum linecount;
|
|
linelog_linenum maxlinecount;
|
|
} linelog_annotateresult;
|
|
|
|
/* free memory used by ar, useful to reset ar from an invalid state */
|
|
void linelog_annotateresult_clear(linelog_annotateresult *ar);
|
|
|
|
/* (re-)initialize the buffer, make it represent an empty file */
|
|
linelog_result linelog_clear(linelog_buf *buf);
|
|
|
|
/* get the actual size needed for buf->data */
|
|
size_t linelog_getactualsize(const linelog_buf *buf);
|
|
|
|
/* get the max revision number covered by this linelog
|
|
return 0 if buf is not initialized (by linelog_clear). */
|
|
linelog_revnum linelog_getmaxrev(const linelog_buf *buf);
|
|
|
|
/* note: some notations are from Python:
|
|
- range(p, q) means from p (inclusive) to q (exclusive), p <= q
|
|
- array[p:q] means a slice of the array with indexes in range(p, q) */
|
|
|
|
/* calculate annotateresult for rev from buf, output result to ar
|
|
|
|
on success, let i be the line number at rev, in range(0, ar->linecount),
|
|
ar->lines[i].rev is the number of the revision introducing the line
|
|
ar->lines[i].linenum is the corresponding line number at ar->lines[i].rev
|
|
|
|
on error, ar may be in an invalid state and needs to be cleared */
|
|
linelog_result linelog_annotate(const linelog_buf *buf,
|
|
linelog_annotateresult *ar, linelog_revnum rev);
|
|
|
|
/* update buf and ar, replace existing lines[a1:a2] with lines[b1:b2] in brev
|
|
|
|
ar should be obtained using linelog_annotate(brev).
|
|
brev introduces the change. the change is not present in earlier revisions.
|
|
|
|
usually brev is greater than maxrev to do incremental updates, like:
|
|
rev = linelog_getmaxrev(buf)
|
|
linelog_annotate(buf, rev, ar)
|
|
for-each-new-rev {
|
|
rev += 1
|
|
// no need to run linelog_annotate(buf, rev, ar) again, because
|
|
// linelog_replacelines will keep it updated
|
|
for-each-chunk {
|
|
linelog_replacelines(buf, ar, rev, ...)
|
|
}
|
|
}
|
|
|
|
however, it's also possible to edit previous revisions, but be sure to use
|
|
the corresponding ar, obtained by calling linelog_annotate(brev).
|
|
|
|
on error, ar may be in an invalid state and needs to be cleared */
|
|
linelog_result linelog_replacelines(linelog_buf *buf,
|
|
linelog_annotateresult *ar, linelog_revnum brev,
|
|
linelog_linenum a1, linelog_linenum a2,
|
|
linelog_linenum b1, linelog_linenum b2);
|
|
|
|
/* like linelog_replacelines, but control details about lines being inserted
|
|
|
|
line numbers and revision numbers are decided by blinenums and brevs.
|
|
this table shows the difference from linelog_replacelines:
|
|
|
|
# | linelog_replacelines | linelog_replacelines_vec
|
|
| revnum, linenum | revnum, linenum
|
|
--+----------------------+----------------------------------------------
|
|
0 | rev, b1 | brevs[0], blinenums[0]
|
|
1 | rev, b1+1 | brevs[1], blinenums[1]
|
|
. | |
|
|
. | rev, b2-1 | brevs[blinecount-1], blinenums[blinecount-1]
|
|
|
|
note: although lines can have revision numbers other than brev, they are
|
|
still marked as introduced by brev. i.e. visible to brev and later
|
|
revisions, invisible to earlier revisions.
|
|
|
|
this is useful for merge commits. consider the following case where rev 3
|
|
merges rev 1 and 2:
|
|
|
|
2 : feature branch
|
|
/ \
|
|
0 -- 1 - 3 -- : main branch
|
|
|
|
a typical "annotate" operation running at rev 3 would show rev 1 and 2 but
|
|
hide rev 3 if the merge is clean.
|
|
|
|
linelog can only store linear history. typically it only tracks the main
|
|
branch thus rev 2 won't get stored. when introducing rev 3 (brev = 3),
|
|
individual lines can have different revisions (brevs[i] != 3) so
|
|
linelog_annotate(rev=3) works as if rev 2 is stored. be aware that
|
|
linelog_annotate(rev=2) will be the same as linelog_annotate(rev=1). */
|
|
linelog_result linelog_replacelines_vec(linelog_buf *buf,
|
|
linelog_annotateresult *ar, linelog_revnum brev,
|
|
linelog_linenum a1, linelog_linenum a2,
|
|
linelog_linenum blinecount, const linelog_revnum *brevs,
|
|
const linelog_linenum *blinenums);
|
|
|
|
/* get all lines, include deleted ones, output to ar
|
|
|
|
offsets can be obtained from annotateresult. if they are both 0,
|
|
all lines from the entire linelog will be returned.
|
|
|
|
internally, this is a traversal from offset1 (inclusive) to offset2
|
|
(exclusive) and conditional jumps are ignored. */
|
|
linelog_result linelog_getalllines(linelog_buf *buf,
|
|
linelog_annotateresult *ar, linelog_offset offset1,
|
|
linelog_offset offset2);
|
|
|
|
#endif /* end of include guard: LINELOG_H_ZUJREV4L */
|