pmt: initial 3.0.2 update
This commit is contained in:
538
jni/parted/libparted/cs/constraint.c
Executable file
538
jni/parted/libparted/cs/constraint.c
Executable file
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
libparted - a library for manipulating disk partitions
|
||||
Copyright (C) 2000-2001, 2007, 2009-2014, 2019-2023 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \addtogroup PedConstraint
|
||||
*
|
||||
* \brief Constraint solver interface.
|
||||
*
|
||||
* Constraints are used to communicate restrictions on operations Constraints
|
||||
* are restrictions on the location and alignment of the start and end of a
|
||||
* partition, and the minimum and maximum size.
|
||||
*
|
||||
* Constraints are closed under intersection (for the proof see the source
|
||||
* code). For background information see the Chinese Remainder Theorem.
|
||||
*
|
||||
* This interface consists of construction constraints, finding the intersection
|
||||
* of constraints, and finding solutions to constraints.
|
||||
*
|
||||
* The constraint solver allows you to specify constraints on where a partition
|
||||
* or file system (or any PedGeometry) may be placed/resized/etc. For example,
|
||||
* you might want to make sure that a file system is at least 10 Gb, or that it
|
||||
* starts at the beginning of new cylinder.
|
||||
*
|
||||
* The constraint solver in this file unifies solver in geom.c (which allows you
|
||||
* to specify constraints on ranges) and natmath.c (which allows you to specify
|
||||
* alignment constraints).
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <parted/parted.h>
|
||||
#include <parted/debug.h>
|
||||
#include <assert.h>
|
||||
|
||||
/**
|
||||
* Initializes a pre-allocated piece of memory to contain a constraint
|
||||
* with the supplied default values.
|
||||
*
|
||||
* \return \c 0 on failure.
|
||||
*/
|
||||
int
|
||||
ped_constraint_init (
|
||||
PedConstraint* constraint,
|
||||
const PedAlignment* start_align,
|
||||
const PedAlignment* end_align,
|
||||
const PedGeometry* start_range,
|
||||
const PedGeometry* end_range,
|
||||
PedSector min_size,
|
||||
PedSector max_size)
|
||||
{
|
||||
PED_ASSERT (constraint != NULL);
|
||||
PED_ASSERT (start_range != NULL);
|
||||
PED_ASSERT (end_range != NULL);
|
||||
PED_ASSERT (min_size > 0);
|
||||
PED_ASSERT (max_size > 0);
|
||||
|
||||
constraint->start_align = ped_alignment_duplicate (start_align);
|
||||
constraint->end_align = ped_alignment_duplicate (end_align);
|
||||
constraint->start_range = ped_geometry_duplicate (start_range);
|
||||
constraint->end_range = ped_geometry_duplicate (end_range);
|
||||
constraint->min_size = min_size;
|
||||
constraint->max_size = max_size;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper for ped_constraint_init().
|
||||
*
|
||||
* Allocates a new piece of memory and initializes the constraint.
|
||||
*
|
||||
* \return \c NULL on failure.
|
||||
*/
|
||||
PedConstraint*
|
||||
ped_constraint_new (
|
||||
const PedAlignment* start_align,
|
||||
const PedAlignment* end_align,
|
||||
const PedGeometry* start_range,
|
||||
const PedGeometry* end_range,
|
||||
PedSector min_size,
|
||||
PedSector max_size)
|
||||
{
|
||||
PedConstraint* constraint;
|
||||
|
||||
constraint = (PedConstraint*) ped_malloc (sizeof (PedConstraint));
|
||||
if (!constraint)
|
||||
goto error;
|
||||
if (!ped_constraint_init (constraint, start_align, end_align,
|
||||
start_range, end_range, min_size, max_size))
|
||||
goto error_free_constraint;
|
||||
return constraint;
|
||||
|
||||
error_free_constraint:
|
||||
free (constraint);
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a constraint that requires a region to be entirely contained inside
|
||||
* \p max, and to entirely contain \p min.
|
||||
*
|
||||
* \return \c NULL on failure.
|
||||
*/
|
||||
PedConstraint*
|
||||
ped_constraint_new_from_min_max (
|
||||
const PedGeometry* min,
|
||||
const PedGeometry* max)
|
||||
{
|
||||
PedGeometry start_range;
|
||||
PedGeometry end_range;
|
||||
|
||||
PED_ASSERT (min != NULL);
|
||||
PED_ASSERT (max != NULL);
|
||||
PED_ASSERT (ped_geometry_test_inside (max, min));
|
||||
|
||||
ped_geometry_init (&start_range, min->dev, max->start,
|
||||
min->start - max->start + 1);
|
||||
ped_geometry_init (&end_range, min->dev, min->end,
|
||||
max->end - min->end + 1);
|
||||
|
||||
return ped_constraint_new (
|
||||
ped_alignment_any, ped_alignment_any,
|
||||
&start_range, &end_range,
|
||||
min->length, max->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a constraint that requires a region to entirely contain \p min.
|
||||
*
|
||||
* \return \c NULL on failure.
|
||||
*/
|
||||
PedConstraint*
|
||||
ped_constraint_new_from_min (const PedGeometry* min)
|
||||
{
|
||||
PedGeometry full_dev;
|
||||
|
||||
PED_ASSERT (min != NULL);
|
||||
|
||||
ped_geometry_init (&full_dev, min->dev, 0, min->dev->length);
|
||||
return ped_constraint_new_from_min_max (min, &full_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a constraint that requires a region to be entirely contained inside
|
||||
* \p max.
|
||||
*
|
||||
* \return \c NULL on failure.
|
||||
*/
|
||||
PedConstraint*
|
||||
ped_constraint_new_from_max (const PedGeometry* max)
|
||||
{
|
||||
PED_ASSERT (max != NULL);
|
||||
|
||||
return ped_constraint_new (
|
||||
ped_alignment_any, ped_alignment_any,
|
||||
max, max, 1, max->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a constraint.
|
||||
*
|
||||
* \return \c NULL on failure.
|
||||
*/
|
||||
PedConstraint*
|
||||
ped_constraint_duplicate (const PedConstraint* constraint)
|
||||
{
|
||||
PED_ASSERT (constraint != NULL);
|
||||
|
||||
return ped_constraint_new (
|
||||
constraint->start_align,
|
||||
constraint->end_align,
|
||||
constraint->start_range,
|
||||
constraint->end_range,
|
||||
constraint->min_size,
|
||||
constraint->max_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a constraint that requires a region to satisfy both \p a and \p b.
|
||||
*
|
||||
* Moreover, any region satisfying \p a and \p b will also satisfy the returned
|
||||
* constraint.
|
||||
*
|
||||
* \return \c NULL if no solution could be found (note that \c NULL is a valid
|
||||
* PedConstraint).
|
||||
*/
|
||||
PedConstraint*
|
||||
ped_constraint_intersect (const PedConstraint* a, const PedConstraint* b)
|
||||
{
|
||||
PedAlignment* start_align;
|
||||
PedAlignment* end_align;
|
||||
PedGeometry* start_range;
|
||||
PedGeometry* end_range;
|
||||
PedSector min_size;
|
||||
PedSector max_size;
|
||||
PedConstraint* constraint;
|
||||
|
||||
if (!a || !b)
|
||||
return NULL;
|
||||
|
||||
start_align = ped_alignment_intersect (a->start_align, b->start_align);
|
||||
if (!start_align)
|
||||
goto empty;
|
||||
end_align = ped_alignment_intersect (a->end_align, b->end_align);
|
||||
if (!end_align)
|
||||
goto empty_destroy_start_align;
|
||||
start_range = ped_geometry_intersect (a->start_range, b->start_range);
|
||||
if (!start_range)
|
||||
goto empty_destroy_end_align;
|
||||
end_range = ped_geometry_intersect (a->end_range, b->end_range);
|
||||
if (!end_range)
|
||||
goto empty_destroy_start_range;
|
||||
min_size = PED_MAX (a->min_size, b->min_size);
|
||||
max_size = PED_MIN (a->max_size, b->max_size);
|
||||
|
||||
constraint = ped_constraint_new (
|
||||
start_align, end_align, start_range, end_range,
|
||||
min_size, max_size);
|
||||
if (!constraint)
|
||||
goto empty_destroy_end_range;
|
||||
|
||||
ped_alignment_destroy (start_align);
|
||||
ped_alignment_destroy (end_align);
|
||||
ped_geometry_destroy (start_range);
|
||||
ped_geometry_destroy (end_range);
|
||||
return constraint;
|
||||
|
||||
empty_destroy_end_range:
|
||||
ped_geometry_destroy (end_range);
|
||||
empty_destroy_start_range:
|
||||
ped_geometry_destroy (start_range);
|
||||
empty_destroy_end_align:
|
||||
ped_alignment_destroy (end_align);
|
||||
empty_destroy_start_align:
|
||||
ped_alignment_destroy (start_align);
|
||||
empty:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the memory allocated for a PedConstraint constructed with
|
||||
* ped_constraint_init().
|
||||
*/
|
||||
void
|
||||
ped_constraint_done (PedConstraint* constraint)
|
||||
{
|
||||
PED_ASSERT (constraint != NULL);
|
||||
|
||||
ped_alignment_destroy (constraint->start_align);
|
||||
ped_alignment_destroy (constraint->end_align);
|
||||
ped_geometry_destroy (constraint->start_range);
|
||||
ped_geometry_destroy (constraint->end_range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the memory allocated for a PedConstraint constructed with
|
||||
* ped_constraint_new().
|
||||
*/
|
||||
void
|
||||
ped_constraint_destroy (PedConstraint* constraint)
|
||||
{
|
||||
if (constraint) {
|
||||
ped_constraint_done (constraint);
|
||||
free (constraint);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the region within which the start must lie
|
||||
* in order to satisfy a constriant. It takes into account
|
||||
* constraint->start_range, constraint->min_size and constraint->max_size.
|
||||
* All sectors in this range that also satisfy alignment requirements have
|
||||
* an end, such that the (start, end) satisfy the constraint.
|
||||
*/
|
||||
static PedGeometry*
|
||||
_constraint_get_canonical_start_range (const PedConstraint* constraint)
|
||||
{
|
||||
PedSector first_end_soln;
|
||||
PedSector last_end_soln;
|
||||
PedSector min_start;
|
||||
PedSector max_start;
|
||||
PedGeometry start_min_max_range;
|
||||
|
||||
if (constraint->min_size > constraint->max_size)
|
||||
return NULL;
|
||||
|
||||
first_end_soln = ped_alignment_align_down (
|
||||
constraint->end_align, constraint->end_range,
|
||||
constraint->end_range->start);
|
||||
last_end_soln = ped_alignment_align_up (
|
||||
constraint->end_align, constraint->end_range,
|
||||
constraint->end_range->end);
|
||||
if (first_end_soln == -1 || last_end_soln == -1
|
||||
|| first_end_soln > last_end_soln
|
||||
|| last_end_soln < constraint->min_size)
|
||||
return NULL;
|
||||
|
||||
min_start = first_end_soln - constraint->max_size + 1;
|
||||
if (min_start < 0)
|
||||
min_start = 0;
|
||||
max_start = last_end_soln - constraint->min_size + 1;
|
||||
if (max_start < 0)
|
||||
return NULL;
|
||||
|
||||
ped_geometry_init (
|
||||
&start_min_max_range, constraint->start_range->dev,
|
||||
min_start, max_start - min_start + 1);
|
||||
|
||||
return ped_geometry_intersect (&start_min_max_range,
|
||||
constraint->start_range);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the nearest start that will have at least one other end that
|
||||
* together satisfy the constraint.
|
||||
*/
|
||||
static PedSector
|
||||
_constraint_get_nearest_start_soln (const PedConstraint* constraint,
|
||||
PedSector start)
|
||||
{
|
||||
PedGeometry* start_range;
|
||||
PedSector result;
|
||||
|
||||
start_range = _constraint_get_canonical_start_range (constraint);
|
||||
if (!start_range)
|
||||
return -1;
|
||||
result = ped_alignment_align_nearest (
|
||||
constraint->start_align, start_range, start);
|
||||
ped_geometry_destroy (start_range);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a constraint and a start ("half of the solution"), find the
|
||||
* range of all possible ends, such that all (start, end) are solutions
|
||||
* to constraint (subject to additional alignment requirements).
|
||||
*/
|
||||
static PedGeometry*
|
||||
_constraint_get_end_range (const PedConstraint* constraint, PedSector start)
|
||||
{
|
||||
PedDevice* dev = constraint->end_range->dev;
|
||||
PedSector first_min_max_end;
|
||||
PedSector last_min_max_end;
|
||||
PedGeometry end_min_max_range;
|
||||
|
||||
if (start + constraint->min_size - 1 > dev->length - 1)
|
||||
return NULL;
|
||||
|
||||
first_min_max_end = start + constraint->min_size - 1;
|
||||
last_min_max_end = start + constraint->max_size - 1;
|
||||
if (last_min_max_end > dev->length - 1)
|
||||
last_min_max_end = dev->length - 1;
|
||||
|
||||
ped_geometry_init (&end_min_max_range, dev,
|
||||
first_min_max_end,
|
||||
last_min_max_end - first_min_max_end + 1);
|
||||
|
||||
return ped_geometry_intersect (&end_min_max_range,
|
||||
constraint->end_range);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given "constraint" and "start", find the end that is nearest to
|
||||
* "end", such that ("start", the end) together form a solution to
|
||||
* "constraint".
|
||||
*/
|
||||
static PedSector
|
||||
_constraint_get_nearest_end_soln (const PedConstraint* constraint,
|
||||
PedSector start, PedSector end)
|
||||
{
|
||||
PedGeometry* end_range;
|
||||
PedSector result;
|
||||
|
||||
end_range = _constraint_get_end_range (constraint, start);
|
||||
if (!end_range)
|
||||
return -1;
|
||||
|
||||
result = ped_alignment_align_nearest (constraint->end_align, end_range,
|
||||
end);
|
||||
ped_geometry_destroy (end_range);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the nearest region to \p geom that satisfy a \p constraint.
|
||||
*
|
||||
* Note that "nearest" is somewhat ambiguous. This function makes
|
||||
* no guarantees about how this ambiguity is resovled.
|
||||
*
|
||||
* \return PedGeometry, or NULL when a \p constrain cannot be satisfied
|
||||
*/
|
||||
PedGeometry*
|
||||
ped_constraint_solve_nearest (
|
||||
const PedConstraint* constraint, const PedGeometry* geom)
|
||||
{
|
||||
PedSector start;
|
||||
PedSector end;
|
||||
PedGeometry* result;
|
||||
|
||||
if (constraint == NULL)
|
||||
return NULL;
|
||||
|
||||
PED_ASSERT (geom != NULL);
|
||||
PED_ASSERT (constraint->start_range->dev == geom->dev);
|
||||
|
||||
start = _constraint_get_nearest_start_soln (constraint, geom->start);
|
||||
if (start == -1)
|
||||
return NULL;
|
||||
end = _constraint_get_nearest_end_soln (constraint, start, geom->end);
|
||||
if (end == -1)
|
||||
return NULL;
|
||||
|
||||
result = ped_geometry_new (geom->dev, start, end - start + 1);
|
||||
if (!result)
|
||||
return NULL;
|
||||
PED_ASSERT (ped_constraint_is_solution (constraint, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the largest region that satisfies a constraint.
|
||||
*
|
||||
* There might be more than one solution. This function makes no
|
||||
* guarantees about which solution it will choose in this case.
|
||||
*/
|
||||
PedGeometry*
|
||||
ped_constraint_solve_max (const PedConstraint* constraint)
|
||||
{
|
||||
PedDevice* dev;
|
||||
PedGeometry full_dev;
|
||||
|
||||
if (!constraint)
|
||||
return NULL;
|
||||
dev = constraint->start_range->dev;
|
||||
ped_geometry_init (&full_dev, dev, 0, dev->length);
|
||||
return ped_constraint_solve_nearest (constraint, &full_dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether \p geom satisfies the given constraint.
|
||||
*
|
||||
* \return \c 1 if it does.
|
||||
**/
|
||||
int
|
||||
ped_constraint_is_solution (const PedConstraint* constraint,
|
||||
const PedGeometry* geom)
|
||||
{
|
||||
PED_ASSERT (constraint != NULL);
|
||||
PED_ASSERT (geom != NULL);
|
||||
|
||||
if (!ped_alignment_is_aligned (constraint->start_align, NULL,
|
||||
geom->start))
|
||||
return 0;
|
||||
if (!ped_alignment_is_aligned (constraint->end_align, NULL, geom->end))
|
||||
return 0;
|
||||
if (!ped_geometry_test_sector_inside (constraint->start_range,
|
||||
geom->start))
|
||||
return 0;
|
||||
if (!ped_geometry_test_sector_inside (constraint->end_range, geom->end))
|
||||
return 0;
|
||||
if (geom->length < constraint->min_size)
|
||||
return 0;
|
||||
if (geom->length > constraint->max_size)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a constraint that any region on the given device will satisfy.
|
||||
*/
|
||||
PedConstraint*
|
||||
ped_constraint_any (const PedDevice* dev)
|
||||
{
|
||||
PedGeometry full_dev;
|
||||
|
||||
if (!ped_geometry_init (&full_dev, dev, 0, dev->length))
|
||||
return NULL;
|
||||
|
||||
return ped_constraint_new (
|
||||
ped_alignment_any,
|
||||
ped_alignment_any,
|
||||
&full_dev,
|
||||
&full_dev,
|
||||
1,
|
||||
dev->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a constraint that only the given region will satisfy.
|
||||
*/
|
||||
PedConstraint*
|
||||
ped_constraint_exact (const PedGeometry* geom)
|
||||
{
|
||||
PedAlignment start_align;
|
||||
PedAlignment end_align;
|
||||
PedGeometry start_sector;
|
||||
PedGeometry end_sector;
|
||||
int ok;
|
||||
|
||||
/* With grain size of 0, it always succeeds. */
|
||||
ok = ped_alignment_init (&start_align, geom->start, 0);
|
||||
assert (ok);
|
||||
ok = ped_alignment_init (&end_align, geom->end, 0);
|
||||
assert (ok);
|
||||
|
||||
ok = ped_geometry_init (&start_sector, geom->dev, geom->start, 1);
|
||||
if (!ok)
|
||||
return NULL;
|
||||
ok = ped_geometry_init (&end_sector, geom->dev, geom->end, 1);
|
||||
if (!ok)
|
||||
return NULL;
|
||||
|
||||
return ped_constraint_new (&start_align, &end_align,
|
||||
&start_sector, &end_sector, 1,
|
||||
geom->dev->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
Reference in New Issue
Block a user