Commit 59cac26b for xz

commit 59cac26b8002377fb0d931e9951607d0b84bc67d
Author: Lasse Collin <lasse.collin@tukaani.org>
Date:   Sat Aug 15 19:42:55 2026 +0300

    liblzma: index.c 1/4: Add bias members

    In this commit the biases are still constant.

diff --git a/src/liblzma/common/index.c b/src/liblzma/common/index.c
index 0492f960..ceaa9222 100644
--- a/src/liblzma/common/index.c
+++ b/src/liblzma/common/index.c
@@ -36,12 +36,14 @@
 /// \brief      Base structure for index_stream and index_group structures
 typedef struct index_tree_node_s index_tree_node;
 struct index_tree_node_s {
-	/// Uncompressed start offset of this Stream (relative to the
-	/// beginning of the file) or Block (relative to the beginning
-	/// of the Stream)
+	/// Uncompressed start offset of this Stream (relative to
+	/// lzma_index.uncompressed_bias) or Block (relative to
+	/// the beginning of the Stream)
 	lzma_vli uncompressed_base;

-	/// Compressed start offset of this Stream or Block
+	/// Compressed start offset of this Stream (relative to
+	/// lzma_index.compressed_bias) or Block (relative to
+	/// the beginning of the Stream)
 	lzma_vli compressed_base;

 	index_tree_node *parent;
@@ -119,7 +121,8 @@ typedef struct {
 	/// Number of this Stream (first one is 1)
 	uint32_t number;

-	/// Total number of Blocks before this Stream
+	/// Total number of Blocks before this Stream relative to
+	/// lzma_index.block_number_base.
 	lzma_vli block_number_base;

 	/// Record groups of this Stream are stored in a tree.
@@ -156,6 +159,15 @@ struct lzma_index_s {
 	/// are many concatenated Streams.
 	index_tree streams;

+	/// In index_stream, node.uncompressed_base, node.compressed_base,
+	/// and block_number_base are relative to these _bias members
+	/// instead of being relative to 0. If a new index_stream is
+	/// prepended before the existing index_streams, the offsets of all
+	/// existing index_streams can be changed by updating these _biases.
+	lzma_vli uncompressed_bias;
+	lzma_vli compressed_bias;
+	lzma_vli block_number_bias;
+
 	/// Uncompressed size of all the Blocks in the Stream(s)
 	lzma_vli uncompressed_size;

@@ -325,9 +337,6 @@ index_tree_locate(const index_tree *tree, lzma_vli target)
 	const index_tree_node *result = NULL;
 	const index_tree_node *node = tree->root;

-	assert(tree->leftmost == NULL
-			|| tree->leftmost->uncompressed_base == 0);
-
 	// Consecutive nodes may have the same uncompressed_base.
 	// We must pick the rightmost one.
 	while (node != NULL) {
@@ -390,6 +399,9 @@ index_init_plain(const lzma_allocator *allocator)
 	lzma_index *i = lzma_alloc(sizeof(lzma_index), allocator);
 	if (i != NULL) {
 		index_tree_init(&i->streams);
+		i->uncompressed_bias = LZMA_VLI_MAX;
+		i->compressed_bias = LZMA_VLI_MAX;
+		i->block_number_bias = LZMA_VLI_MAX;
 		i->uncompressed_size = 0;
 		i->total_size = 0;
 		i->record_count = 0;
@@ -409,7 +421,9 @@ lzma_index_init(const lzma_allocator *allocator)
 	if (i == NULL)
 		return NULL;

-	index_stream *s = index_stream_init(0, 0, 1, 0, allocator);
+	index_stream *s = index_stream_init(
+			i->compressed_bias, i->uncompressed_bias,
+			1, i->block_number_bias, allocator);
 	if (s == NULL) {
 		lzma_free(i, allocator);
 		return NULL;
@@ -587,7 +601,7 @@ lzma_index_file_size(const lzma_index *i)
 {
 	const index_stream *s = (const index_stream *)(i->streams.rightmost);
 	const index_group *g = (const index_group *)(s->groups.rightmost);
-	return index_file_size(s->node.compressed_base,
+	return index_file_size(s->node.compressed_base - i->compressed_bias,
 			g == NULL ? 0 : g->records[g->last].unpadded_sum,
 			s->record_count, s->index_list_size,
 			s->stream_padding);
@@ -693,7 +707,7 @@ lzma_index_append(lzma_index *i, const lzma_allocator *allocator,
 		return LZMA_DATA_ERROR;

 	// Check that the file size will stay within limits.
-	if (index_file_size(s->node.compressed_base,
+	if (index_file_size(s->node.compressed_base - i->compressed_bias,
 			compressed_base + unpadded_size, s->record_count + 1,
 			s->index_list_size + index_list_size_add,
 			s->stream_padding) == LZMA_VLI_UNKNOWN)
@@ -981,6 +995,9 @@ lzma_index_dup(const lzma_index *src, const lzma_allocator *allocator)
 		return NULL;

 	// Copy the totals.
+	dest->uncompressed_bias = src->uncompressed_bias;
+	dest->compressed_bias = src->compressed_bias;
+	dest->block_number_bias = src->block_number_bias;
 	dest->uncompressed_size = src->uncompressed_size;
 	dest->total_size = src->total_size;
 	dest->record_count = src->record_count;
@@ -1067,8 +1084,10 @@ iter_set_info(lzma_index_iter *iter)
 	// internally.
 	iter->stream.number = stream->number;
 	iter->stream.block_count = stream->record_count;
-	iter->stream.compressed_offset = stream->node.compressed_base;
-	iter->stream.uncompressed_offset = stream->node.uncompressed_base;
+	iter->stream.compressed_offset = stream->node.compressed_base
+			- i->compressed_bias;
+	iter->stream.uncompressed_offset = stream->node.uncompressed_base
+			- i->uncompressed_bias;

 	// iter->stream.flags will be NULL if the Stream Flags haven't been
 	// set with lzma_index_stream_flags().
@@ -1097,7 +1116,7 @@ iter_set_info(lzma_index_iter *iter)
 	if (group != NULL) {
 		iter->block.number_in_stream = group->number_base + record;
 		iter->block.number_in_file = iter->block.number_in_stream
-				+ stream->block_number_base;
+			+ (stream->block_number_base - i->block_number_bias);

 		iter->block.compressed_stream_offset
 				= record == 0 ? group->node.compressed_base
@@ -1262,14 +1281,21 @@ lzma_index_iter_locate(lzma_index_iter *iter, lzma_vli target)
 {
 	const lzma_index *i = iter->internal[ITER_INDEX].p;

+	assert(i->uncompressed_size <= LZMA_VLI_MAX);
+
 	// If the target is past the end of the file, return immediately.
 	if (i->uncompressed_size <= target)
 		return true;

+	assert(target < LZMA_VLI_MAX);
+
 	// Locate the Stream containing the target offset.
+	// NOTE: Adding the bias can make target >= LZMA_VLI_MAX.
+	target += i->uncompressed_bias;
 	const index_stream *stream = index_tree_locate(&i->streams, target);
 	assert(stream != NULL);
 	target -= stream->node.uncompressed_base;
+	assert(target < LZMA_VLI_MAX);

 	// Locate the group containing the target offset.
 	const index_group *group = index_tree_locate(&stream->groups, target);