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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
487
jni/parted/libparted/cs/geom.c
Executable file
487
jni/parted/libparted/cs/geom.c
Executable file
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
libparted - a library for manipulating disk partitions
|
||||
Copyright (C) 1999-2000, 2005, 2007-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/>.
|
||||
*/
|
||||
|
||||
/** \file geom.c */
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup PedGeometry
|
||||
*
|
||||
* \brief PedGeometry represents a continuous region on a device. All addressing
|
||||
* through a PedGeometry object is in terms of the start of the continuous
|
||||
* region.
|
||||
*
|
||||
* The following conditions are always true on a PedGeometry object manipulated
|
||||
* with the GNU Parted API:
|
||||
*
|
||||
* - <tt>start + length - 1 == end</tt>
|
||||
* - <tt>length > 0</tt>
|
||||
* - <tt>start >= 0</tt>
|
||||
* - <tt>end < dev->length</tt>
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <parted/parted.h>
|
||||
#include <parted/debug.h>
|
||||
|
||||
#if ENABLE_NLS
|
||||
# include <libintl.h>
|
||||
# define _(String) dgettext (PACKAGE, String)
|
||||
#else
|
||||
# define _(String) (String)
|
||||
#endif /* ENABLE_NLS */
|
||||
|
||||
/**
|
||||
* Initialize the previously allocated PedGeometry \p geom.
|
||||
*/
|
||||
int
|
||||
ped_geometry_init (PedGeometry* geom, const PedDevice* dev,
|
||||
PedSector start, PedSector length)
|
||||
{
|
||||
PED_ASSERT (geom != NULL);
|
||||
PED_ASSERT (dev != NULL);
|
||||
|
||||
geom->dev = (PedDevice*) dev;
|
||||
return ped_geometry_set (geom, start, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new PedGeometry object on \p disk, starting at \p start with a
|
||||
* size of \p length sectors.
|
||||
*
|
||||
* \return NULL on failure.
|
||||
*/
|
||||
PedGeometry*
|
||||
ped_geometry_new (const PedDevice* dev, PedSector start, PedSector length)
|
||||
{
|
||||
PedGeometry* geom;
|
||||
|
||||
PED_ASSERT (dev != NULL);
|
||||
|
||||
geom = (PedGeometry*) ped_malloc (sizeof (PedGeometry));
|
||||
if (!geom)
|
||||
goto error;
|
||||
if (!ped_geometry_init (geom, dev, start, length))
|
||||
goto error_free_geom;
|
||||
return geom;
|
||||
|
||||
error_free_geom:
|
||||
free (geom);
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a PedGeometry object.
|
||||
*
|
||||
* This function constructs a PedGeometry object that is an identical but
|
||||
* independent copy of \p geom. Both the input, \p geom, and the output
|
||||
* should be destroyed with ped_geometry_destroy() when they are no
|
||||
* longer needed.
|
||||
*
|
||||
* \return NULL on failure.
|
||||
*/
|
||||
PedGeometry*
|
||||
ped_geometry_duplicate (const PedGeometry* geom)
|
||||
{
|
||||
PED_ASSERT (geom != NULL);
|
||||
return ped_geometry_new (geom->dev, geom->start, geom->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a PedGeometry object that refers to the intersection of
|
||||
* \p a and \p b.
|
||||
*
|
||||
* This function constructs a PedGeometry object that describes the
|
||||
* region that is common to both a and b. If there is no such common
|
||||
* region, it returns NULL. (This situation is not treated as an
|
||||
* error by much of GNU Parted.)
|
||||
*/
|
||||
PedGeometry*
|
||||
ped_geometry_intersect (const PedGeometry* a, const PedGeometry* b)
|
||||
{
|
||||
PedSector start;
|
||||
PedSector end;
|
||||
|
||||
if (!a || !b || a->dev != b->dev)
|
||||
return NULL;
|
||||
|
||||
start = PED_MAX (a->start, b->start);
|
||||
end = PED_MIN (a->end, b->end);
|
||||
if (start > end)
|
||||
return NULL;
|
||||
|
||||
return ped_geometry_new (a->dev, start, end - start + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a PedGeometry object.
|
||||
*/
|
||||
void
|
||||
ped_geometry_destroy (PedGeometry* geom)
|
||||
{
|
||||
PED_ASSERT (geom != NULL);
|
||||
|
||||
free (geom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a new \p start, \p end (implicitly) and \p length to \p geom.
|
||||
*
|
||||
* \p geom->end is calculated from \p start and \p length.
|
||||
*/
|
||||
int
|
||||
ped_geometry_set (PedGeometry* geom, PedSector start, PedSector length)
|
||||
{
|
||||
PED_ASSERT (geom != NULL);
|
||||
PED_ASSERT (geom->dev != NULL);
|
||||
PED_ASSERT (start >= 0);
|
||||
|
||||
if (length < 1) {
|
||||
ped_exception_throw (
|
||||
PED_EXCEPTION_ERROR,
|
||||
PED_EXCEPTION_CANCEL,
|
||||
_("Can't have the end before the start!"
|
||||
" (start sector=%jd length=%jd)"), start, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
geom->start = start;
|
||||
geom->length = length;
|
||||
geom->end = start + length - 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a new start to \p geom without changing \p geom->end.
|
||||
*
|
||||
* \p geom->length is updated accordingly.
|
||||
*/
|
||||
int
|
||||
ped_geometry_set_start (PedGeometry* geom, PedSector start)
|
||||
{
|
||||
return ped_geometry_set (geom, start, geom->end - start + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a new end to \p geom without changing \p geom->start.
|
||||
*
|
||||
* \p geom->length is updated accordingly.
|
||||
*/
|
||||
int
|
||||
ped_geometry_set_end (PedGeometry* geom, PedSector end)
|
||||
{
|
||||
return ped_geometry_set (geom, geom->start, end - geom->start + 1);
|
||||
}
|
||||
/**
|
||||
* Test if \p a overlaps with \p b.
|
||||
*
|
||||
* That is, they lie on the same physical device, and they share
|
||||
* the same physical region at least partially.
|
||||
*
|
||||
* \return 1 if \p a and \p b overlap.
|
||||
*/
|
||||
int
|
||||
ped_geometry_test_overlap (const PedGeometry* a, const PedGeometry* b)
|
||||
{
|
||||
PED_ASSERT (a != NULL);
|
||||
PED_ASSERT (b != NULL);
|
||||
|
||||
if (a->dev != b->dev)
|
||||
return 0;
|
||||
|
||||
if (a->start < b->start)
|
||||
return a->end >= b->start;
|
||||
else
|
||||
return b->end >= a->start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if \p b lies completely within \p a. That is, they lie on the same
|
||||
* physical device, and all of the \p b's region is contained inside
|
||||
* \p a's.
|
||||
*
|
||||
* \return 1 if the region \p b describes is contained entirely inside \p a
|
||||
*/
|
||||
int
|
||||
ped_geometry_test_inside (const PedGeometry* a, const PedGeometry* b)
|
||||
{
|
||||
PED_ASSERT (a != NULL);
|
||||
PED_ASSERT (b != NULL);
|
||||
|
||||
if (a->dev != b->dev)
|
||||
return 0;
|
||||
|
||||
return b->start >= a->start && b->end <= a->end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if \a a and \p b refer to the same physical region.
|
||||
*
|
||||
* \return 1 if \p a and \p b describe the same regions
|
||||
*
|
||||
*/
|
||||
int
|
||||
ped_geometry_test_equal (const PedGeometry* a, const PedGeometry* b)
|
||||
{
|
||||
PED_ASSERT (a != NULL);
|
||||
PED_ASSERT (b != NULL);
|
||||
|
||||
return a->dev == b->dev
|
||||
&& a->start == b->start
|
||||
&& a->end == b->end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if \p sector is inside \p geom.
|
||||
*
|
||||
* \return 1 if sector lies within the \p region that \p geom describes
|
||||
*/
|
||||
int
|
||||
ped_geometry_test_sector_inside (const PedGeometry* geom, PedSector sector)
|
||||
{
|
||||
PED_ASSERT (geom != NULL);
|
||||
|
||||
return sector >= geom->start && sector <= geom->end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from the region represented by \p geom. \p offset is the
|
||||
* location from within the region, not from the start of the disk.
|
||||
* \p count sectors are read into \p buffer.
|
||||
* This is essentially equivalent to:
|
||||
* \code
|
||||
* ped_device_read (geom->disk->dev, buffer, geom->start + offset, count)
|
||||
* \endcode
|
||||
*
|
||||
* \throws PED_EXCEPTION_ERROR when attempting to read sectors outside of
|
||||
* partition
|
||||
*
|
||||
* \return 0 on failure
|
||||
*/
|
||||
int
|
||||
ped_geometry_read (const PedGeometry* geom, void* buffer, PedSector offset,
|
||||
PedSector count)
|
||||
{
|
||||
PedSector real_start;
|
||||
|
||||
PED_ASSERT (geom != NULL);
|
||||
PED_ASSERT (buffer != NULL);
|
||||
PED_ASSERT (offset >= 0);
|
||||
PED_ASSERT (count >= 0);
|
||||
|
||||
real_start = geom->start + offset;
|
||||
|
||||
if (real_start + count - 1 > geom->end)
|
||||
return 0;
|
||||
|
||||
if (!ped_device_read (geom->dev, buffer, real_start, count))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Like ped_device_read, but read into malloc'd storage. */
|
||||
int
|
||||
ped_geometry_read_alloc (const PedGeometry* geom, void** buffer,
|
||||
PedSector offset, PedSector count)
|
||||
{
|
||||
char *buf = ped_malloc (count * geom->dev->sector_size);
|
||||
if (buf == NULL)
|
||||
return 0;
|
||||
int ok = ped_geometry_read (geom, buf, offset, count);
|
||||
if (!ok) {
|
||||
free (buf);
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
*buffer = buf;
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the cache on \p geom.
|
||||
*
|
||||
* This function flushes all write-behind caches that might be holding
|
||||
* writes made by ped_geometry_write() to \p geom. It is slow, because
|
||||
* it guarantees cache coherency among all relevant caches.
|
||||
*
|
||||
* \return 0 on failure
|
||||
*/
|
||||
int
|
||||
ped_geometry_sync (PedGeometry* geom)
|
||||
{
|
||||
PED_ASSERT (geom != NULL);
|
||||
return ped_device_sync (geom->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the cache on \p geom.
|
||||
*
|
||||
* This function flushes all write-behind caches that might be holding writes
|
||||
* made by ped_geometry_write() to \p geom. It does NOT ensure cache coherency
|
||||
* with other caches that cache data in the region described by \p geom.
|
||||
* If you need cache coherency, use ped_geometry_sync() instead.
|
||||
*
|
||||
* \return 0 on failure
|
||||
*/
|
||||
int
|
||||
ped_geometry_sync_fast (PedGeometry* geom)
|
||||
{
|
||||
PED_ASSERT (geom != NULL);
|
||||
return ped_device_sync_fast (geom->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes data into the region represented by \p geom. \p offset is the
|
||||
* location from within the region, not from the start of the disk.
|
||||
* \p count sectors are written.
|
||||
*
|
||||
* \return 0 on failure
|
||||
*/
|
||||
int
|
||||
ped_geometry_write (PedGeometry* geom, const void* buffer, PedSector offset,
|
||||
PedSector count)
|
||||
{
|
||||
int exception_status;
|
||||
PedSector real_start;
|
||||
|
||||
PED_ASSERT (geom != NULL);
|
||||
PED_ASSERT (buffer != NULL);
|
||||
PED_ASSERT (offset >= 0);
|
||||
PED_ASSERT (count >= 0);
|
||||
|
||||
real_start = geom->start + offset;
|
||||
|
||||
if (real_start + count - 1 > geom->end) {
|
||||
exception_status = ped_exception_throw (
|
||||
PED_EXCEPTION_ERROR,
|
||||
PED_EXCEPTION_IGNORE_CANCEL,
|
||||
_("Attempt to write sectors %ld-%ld outside of "
|
||||
"partition on %s."),
|
||||
(long) offset, (long) (offset + count - 1),
|
||||
geom->dev->path);
|
||||
return exception_status == PED_EXCEPTION_IGNORE;
|
||||
}
|
||||
|
||||
if (!ped_device_write (geom->dev, buffer, real_start, count))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for physical disk errors. \todo use ped_device_check()
|
||||
*
|
||||
* Checks a region for physical defects on \p geom. \p buffer is used
|
||||
* for temporary storage for ped_geometry_check(), and has an undefined
|
||||
* value. \p buffer is \p buffer_size sectors long.
|
||||
* The region checked starts at \p offset sectors inside the
|
||||
* region represented by \p geom, and is \p count sectors long.
|
||||
* \p granularity specificies how sectors should be grouped
|
||||
* together. The first bad sector to be returned will always be in
|
||||
* the form:
|
||||
* <tt>offset + n * granularity</tt>
|
||||
*
|
||||
* \return the first bad sector, or 0 if there were no physical errors
|
||||
*/
|
||||
PedSector
|
||||
ped_geometry_check (PedGeometry* geom, void* buffer, PedSector buffer_size,
|
||||
PedSector offset, PedSector granularity, PedSector count,
|
||||
PedTimer* timer)
|
||||
{
|
||||
PedSector group;
|
||||
PedSector i;
|
||||
PedSector read_len;
|
||||
|
||||
PED_ASSERT (geom != NULL);
|
||||
PED_ASSERT (buffer != NULL);
|
||||
|
||||
ped_timer_reset (timer);
|
||||
ped_timer_set_state_name (timer, _("checking for bad blocks"));
|
||||
|
||||
retry:
|
||||
ped_exception_fetch_all();
|
||||
for (group = offset; group < offset + count; group += buffer_size) {
|
||||
ped_timer_update (timer, 1.0 * (group - offset) / count);
|
||||
read_len = PED_MIN (buffer_size, offset + count - group);
|
||||
if (!ped_geometry_read (geom, buffer, group, read_len))
|
||||
goto found_error;
|
||||
}
|
||||
ped_exception_leave_all();
|
||||
ped_timer_update (timer, 1.0);
|
||||
return 0;
|
||||
|
||||
found_error:
|
||||
ped_exception_catch();
|
||||
for (i = group; i + granularity < group + count; i += granularity) {
|
||||
if (!ped_geometry_read (geom, buffer, i, granularity)) {
|
||||
ped_exception_catch();
|
||||
ped_exception_leave_all();
|
||||
return i;
|
||||
}
|
||||
}
|
||||
ped_exception_leave_all();
|
||||
goto retry; /* weird: failure on group read, but not individually */
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes a \p sector inside the region described by src, and
|
||||
* returns that sector's address inside dst. This means that
|
||||
*
|
||||
* \code
|
||||
* ped_geometry_read (dst, buf, ped_geometry_map(dst, src, sector), 1)
|
||||
* \endcode
|
||||
*
|
||||
* does the same thing as
|
||||
*
|
||||
* \code
|
||||
* ped_geometry_read (src, buf, sector, 1)
|
||||
* \endcode
|
||||
*
|
||||
* Clearly, this will only work if \p src and \p dst overlap.
|
||||
*
|
||||
* \return -1 if \p sector is not within \p dst's space,
|
||||
* or \p sector's address inside \p dst
|
||||
*
|
||||
*/
|
||||
PedSector
|
||||
ped_geometry_map (const PedGeometry* dst, const PedGeometry* src,
|
||||
PedSector sector)
|
||||
{
|
||||
PedSector result;
|
||||
|
||||
PED_ASSERT (dst != NULL);
|
||||
PED_ASSERT (src != NULL);
|
||||
|
||||
if (!ped_geometry_test_sector_inside (src, sector))
|
||||
return -1;
|
||||
if (dst->dev != src->dev)
|
||||
return -1;
|
||||
|
||||
result = src->start + sector - dst->start;
|
||||
if (result < 0 || result > dst->length)
|
||||
return -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
481
jni/parted/libparted/cs/natmath.c
Executable file
481
jni/parted/libparted/cs/natmath.c
Executable file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
libparted - a library for manipulating disk partitions
|
||||
Copyright (C) 2000, 2007-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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file natmath.c
|
||||
*/
|
||||
|
||||
/**
|
||||
* \addtogroup PedAlignment
|
||||
*
|
||||
* \brief Alignment constraint model.
|
||||
*
|
||||
* This part of libparted models alignment constraints.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
#include <parted/parted.h>
|
||||
#include <parted/debug.h>
|
||||
#include <parted/natmath.h>
|
||||
|
||||
/* Arrrghhh! Why doesn't C have tuples? */
|
||||
typedef struct {
|
||||
PedSector gcd; /* "converges" to the gcd */
|
||||
PedSector x;
|
||||
PedSector y;
|
||||
} EuclidTriple;
|
||||
|
||||
static const PedAlignment _any = {
|
||||
offset: 0,
|
||||
grain_size: 1
|
||||
};
|
||||
|
||||
const PedAlignment* ped_alignment_any = &_any;
|
||||
const PedAlignment* ped_alignment_none = NULL;
|
||||
|
||||
/* This function returns "a mod b", the way C should have done it!
|
||||
* Mathematicians prefer -3 mod 4 to be 3. Reason: division by N
|
||||
* is all about adding or subtracting N, and we like our remainders
|
||||
* to be between 0 and N - 1.
|
||||
*/
|
||||
static PedSector
|
||||
abs_mod (PedSector a, PedSector b)
|
||||
{
|
||||
if (a < 0)
|
||||
return a % b + b;
|
||||
else
|
||||
return a % b;
|
||||
}
|
||||
|
||||
/* Rounds a number down to the closest number that is a multiple of
|
||||
* grain_size.
|
||||
*/
|
||||
PedSector
|
||||
ped_round_down_to (PedSector sector, PedSector grain_size)
|
||||
{
|
||||
return sector - abs_mod (sector, grain_size);
|
||||
}
|
||||
|
||||
/* Rounds a number up to the closest number that is a multiple of
|
||||
* grain_size.
|
||||
*/
|
||||
PedSector
|
||||
ped_round_up_to (PedSector sector, PedSector grain_size)
|
||||
{
|
||||
if (sector % grain_size)
|
||||
return ped_round_down_to (sector, grain_size) + grain_size;
|
||||
else
|
||||
return sector;
|
||||
}
|
||||
|
||||
/* Rounds a number to the closest number that is a multiple of grain_size. */
|
||||
PedSector
|
||||
ped_round_to_nearest (PedSector sector, PedSector grain_size)
|
||||
{
|
||||
if (sector % grain_size > grain_size/2)
|
||||
return ped_round_up_to (sector, grain_size);
|
||||
else
|
||||
return ped_round_down_to (sector, grain_size);
|
||||
}
|
||||
|
||||
/* This function returns the largest number that divides both a and b.
|
||||
* It uses the ancient Euclidean algorithm.
|
||||
*/
|
||||
PedSector
|
||||
ped_greatest_common_divisor (PedSector a, PedSector b)
|
||||
{
|
||||
PED_ASSERT (a >= 0);
|
||||
PED_ASSERT (b >= 0);
|
||||
|
||||
/* Put the arguments in the "right" format. (Recursive calls made by
|
||||
* this function are always in the right format.)
|
||||
*/
|
||||
if (b > a)
|
||||
return ped_greatest_common_divisor (b, a);
|
||||
|
||||
if (b)
|
||||
return ped_greatest_common_divisor (b, a % b);
|
||||
else
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a preallocated piece of memory for an alignment object
|
||||
* (used by PedConstraint).
|
||||
*
|
||||
* The object will represent all sectors \e s for which the equation
|
||||
* <tt>s = offset + X * grain_size</tt> holds.
|
||||
*/
|
||||
int
|
||||
ped_alignment_init (PedAlignment* align, PedSector offset, PedSector grain_size)
|
||||
{
|
||||
PED_ASSERT (align != NULL);
|
||||
|
||||
if (grain_size < 0)
|
||||
return 0;
|
||||
|
||||
if (grain_size)
|
||||
align->offset = abs_mod (offset, grain_size);
|
||||
else
|
||||
align->offset = offset;
|
||||
align->grain_size = grain_size;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an alignment object (used by PedConstraint), representing all
|
||||
* PedSector's that are of the form <tt>offset + X * grain_size</tt>.
|
||||
*/
|
||||
PedAlignment*
|
||||
ped_alignment_new (PedSector offset, PedSector grain_size)
|
||||
{
|
||||
PedAlignment* align;
|
||||
|
||||
align = (PedAlignment*) ped_malloc (sizeof (PedAlignment));
|
||||
if (!align)
|
||||
goto error;
|
||||
|
||||
if (!ped_alignment_init (align, offset, grain_size))
|
||||
goto error_free_align;
|
||||
|
||||
return align;
|
||||
|
||||
error_free_align:
|
||||
free (align);
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free up memory associated with \p align.
|
||||
*/
|
||||
void
|
||||
ped_alignment_destroy (PedAlignment* align)
|
||||
{
|
||||
free (align);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a duplicate of \p align.
|
||||
*/
|
||||
PedAlignment*
|
||||
ped_alignment_duplicate (const PedAlignment* align)
|
||||
{
|
||||
if (!align)
|
||||
return NULL;
|
||||
return ped_alignment_new (align->offset, align->grain_size);
|
||||
}
|
||||
|
||||
/* the extended Euclid algorithm.
|
||||
*
|
||||
* input:
|
||||
* a and b, a > b
|
||||
*
|
||||
* output:
|
||||
* gcd, x and y, such that:
|
||||
*
|
||||
* gcd = greatest common divisor of a and b
|
||||
* gcd = x*a + y*b
|
||||
*/
|
||||
static EuclidTriple _GL_ATTRIBUTE_PURE
|
||||
extended_euclid (int a, int b)
|
||||
{
|
||||
EuclidTriple result;
|
||||
EuclidTriple tmp;
|
||||
|
||||
if (b == 0) {
|
||||
result.gcd = a;
|
||||
result.x = 1;
|
||||
result.y = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
tmp = extended_euclid (b, a % b);
|
||||
result.gcd = tmp.gcd;
|
||||
result.x = tmp.y;
|
||||
result.y = tmp.x - (a/b) * tmp.y;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function computes a PedAlignment object that describes the
|
||||
* intersection of two alignments. That is, a sector satisfies the
|
||||
* new alignment object if and only if it satisfies both of the original
|
||||
* ones. (See ped_alignment_is_aligned() for the meaning of "satisfies")
|
||||
*
|
||||
* Apart from the trivial cases (where one or both of the alignment objects
|
||||
* constraints have no sectors that satisfy them), this is what we're trying to
|
||||
* do:
|
||||
* - two input constraints: \p a and \p b.
|
||||
* - the new grain_size is going to be the lowest common multiple of
|
||||
* \p a->grain_size and \p b->grain_size
|
||||
* - hard part - solve the simultaneous equations, for offset, where offset,
|
||||
* X and Y are variables. (Note: offset can be obtained from either X or Y,
|
||||
* by substituing into either equation)
|
||||
*
|
||||
* \code
|
||||
* offset = \p a->offset + X * \p a->grain_size (1)
|
||||
* offset = \p b->offset + Y * \p b->grain_size (2)
|
||||
* \endcode
|
||||
*
|
||||
* or, abbreviated:
|
||||
*
|
||||
* \code
|
||||
* o = Ao + X*Ag (1)
|
||||
* o = Bo + Y*Bg (2)
|
||||
*
|
||||
* => Ao + X*Ag = Bo + Y*Bg (1) = (2)
|
||||
* X*Ag - Y*Bg = Bo - Ao (3)
|
||||
* \endcode
|
||||
*
|
||||
* As it turns out, there only exists a solution if (Bo - Ao) is a multiple
|
||||
* of the GCD of Ag and Bg. Reason: all linear combinations of Ag and Bg are
|
||||
* multiples of the GCD.
|
||||
*
|
||||
* Proof:
|
||||
*
|
||||
* \code
|
||||
* A * Ag + B * Bg
|
||||
* = A * (\p a * gcd) + B * (\p b * gcd)
|
||||
* = gcd * (A * \p a + B * \p b)
|
||||
* \endcode
|
||||
*
|
||||
* gcd is a factor of the linear combination. QED
|
||||
*
|
||||
* Anyway, \p a * Ag + \p b * Bg = gcd can be solved (for \p a, \p b and gcd)
|
||||
* with Euclid's extended algorithm. Then, we just multiply through by
|
||||
* (Bo - Ao) / gcd to get (3).
|
||||
*
|
||||
* i.e.
|
||||
* \code
|
||||
* A * Ag + B * Bg = gcd
|
||||
* A*(Bo-Ao)/gcd * Ag + B(Bo-Ao)/gcd * Bg = gcd * (Bo-Ao)/gcd
|
||||
* X*Ag - Y*Bg = Bo - Ao (3)
|
||||
*
|
||||
* X = A*(Bo-Ao)/gcd
|
||||
* Y = - B*(Bo-Ao)/gcd
|
||||
* \endcode
|
||||
*
|
||||
* then:
|
||||
* \code
|
||||
* o = Ao + X*Ag (1)
|
||||
* = Ao + A*(Bo-Ao)/gcd*Ag
|
||||
* o = Bo + Y*Bg (2)
|
||||
* = Bo - B*(Bo-Ao)/gcd*Ag
|
||||
* \endcode
|
||||
*
|
||||
* Thanks go to Nathan Hurst (njh@hawthorn.csse.monash.edu.au) for figuring
|
||||
* this algorithm out :-)
|
||||
*
|
||||
* \note Returned \c NULL is a valid PedAlignment object, and can be used
|
||||
for ped_alignment_*() function.
|
||||
*
|
||||
* \return a PedAlignment on success, \c NULL on failure
|
||||
*/
|
||||
PedAlignment*
|
||||
ped_alignment_intersect (const PedAlignment* a, const PedAlignment* b)
|
||||
{
|
||||
PedSector new_grain_size;
|
||||
PedSector new_offset;
|
||||
PedSector delta_on_gcd;
|
||||
EuclidTriple gcd_factors;
|
||||
|
||||
|
||||
if (!a || !b)
|
||||
return NULL;
|
||||
|
||||
/*PED_DEBUG (0x10, "intersecting alignments (%d,%d) and (%d,%d)",
|
||||
a->offset, a->grain_size, b->offset, b->grain_size);
|
||||
*/
|
||||
|
||||
if (a->grain_size < b->grain_size) {
|
||||
const PedAlignment* tmp;
|
||||
tmp = a; a = b; b = tmp;
|
||||
}
|
||||
|
||||
/* weird/trivial case: where the solution space for "a" or "b" is
|
||||
* either empty or contains exactly one solution
|
||||
*/
|
||||
if (a->grain_size == 0 && b->grain_size == 0) {
|
||||
if (a->offset == b->offset)
|
||||
return ped_alignment_duplicate (a);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* general case */
|
||||
gcd_factors = extended_euclid (a->grain_size, b->grain_size);
|
||||
|
||||
delta_on_gcd = (b->offset - a->offset) / gcd_factors.gcd;
|
||||
new_offset = a->offset + gcd_factors.x * delta_on_gcd * a->grain_size;
|
||||
new_grain_size = a->grain_size * b->grain_size / gcd_factors.gcd;
|
||||
|
||||
/* inconsistency => no solution */
|
||||
if (new_offset
|
||||
!= b->offset - gcd_factors.y * delta_on_gcd * b->grain_size)
|
||||
return NULL;
|
||||
|
||||
return ped_alignment_new (new_offset, new_grain_size);
|
||||
}
|
||||
|
||||
/* This function returns the sector closest to "sector" that lies inside
|
||||
* geom and satisfies the alignment constraint.
|
||||
*/
|
||||
static PedSector _GL_ATTRIBUTE_PURE
|
||||
_closest_inside_geometry (const PedAlignment* align, const PedGeometry* geom,
|
||||
PedSector sector)
|
||||
{
|
||||
PED_ASSERT (align != NULL);
|
||||
|
||||
if (!align->grain_size) {
|
||||
if (ped_alignment_is_aligned (align, geom, sector)
|
||||
&& (!geom || ped_geometry_test_sector_inside (geom,
|
||||
sector)))
|
||||
return sector;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sector < geom->start)
|
||||
sector += ped_round_up_to (geom->start - sector,
|
||||
align->grain_size);
|
||||
if (sector > geom->end)
|
||||
sector -= ped_round_up_to (sector - geom->end,
|
||||
align->grain_size);
|
||||
|
||||
if (!ped_geometry_test_sector_inside (geom, sector))
|
||||
return -1;
|
||||
return sector;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the closest sector to \p sector that lies inside
|
||||
* \p geom that satisfies the given alignment constraint \p align. It prefers
|
||||
* sectors that are beyond \p sector (are not smaller than \p sector),
|
||||
* but does not guarantee that this.
|
||||
*
|
||||
* \return a PedSector on success, \c -1 on failure
|
||||
*/
|
||||
PedSector
|
||||
ped_alignment_align_up (const PedAlignment* align, const PedGeometry* geom,
|
||||
PedSector sector)
|
||||
{
|
||||
PedSector result;
|
||||
|
||||
PED_ASSERT (align != NULL);
|
||||
|
||||
if (!align->grain_size)
|
||||
result = align->offset;
|
||||
else
|
||||
result = ped_round_up_to (sector - align->offset,
|
||||
align->grain_size)
|
||||
+ align->offset;
|
||||
|
||||
if (geom)
|
||||
result = _closest_inside_geometry (align, geom, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the closest sector to \p sector that lies inside
|
||||
* \p geom that satisfies the given alignment constraint \p align. It prefers
|
||||
* sectors that are before \p sector (are not larger than \p sector),
|
||||
* but does not guarantee that this.
|
||||
*
|
||||
* \return a PedSector on success, \c -1 on failure
|
||||
*/
|
||||
PedSector
|
||||
ped_alignment_align_down (const PedAlignment* align, const PedGeometry* geom,
|
||||
PedSector sector)
|
||||
{
|
||||
PedSector result;
|
||||
|
||||
PED_ASSERT (align != NULL);
|
||||
|
||||
if (!align->grain_size)
|
||||
result = align->offset;
|
||||
else
|
||||
result = ped_round_down_to (sector - align->offset,
|
||||
align->grain_size)
|
||||
+ align->offset;
|
||||
|
||||
if (geom)
|
||||
result = _closest_inside_geometry (align, geom, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Returns either a or b, depending on which is closest to "sector". */
|
||||
static PedSector
|
||||
closest (PedSector sector, PedSector a, PedSector b)
|
||||
{
|
||||
if (a == -1)
|
||||
return b;
|
||||
if (b == -1)
|
||||
return a;
|
||||
|
||||
if (llabs (sector - a) < llabs (sector - b))
|
||||
return a;
|
||||
else
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the sector that is closest to \p sector,
|
||||
* satisfies the \p align constraint and lies inside \p geom.
|
||||
*
|
||||
* \return a PedSector on success, \c -1 on failure
|
||||
*/
|
||||
PedSector
|
||||
ped_alignment_align_nearest (const PedAlignment* align, const PedGeometry* geom,
|
||||
PedSector sector)
|
||||
{
|
||||
PED_ASSERT (align != NULL);
|
||||
|
||||
return closest (sector, ped_alignment_align_up (align, geom, sector),
|
||||
ped_alignment_align_down (align, geom, sector));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns 1 if \p sector satisfies the alignment
|
||||
* constraint \p align and lies inside \p geom.
|
||||
*
|
||||
* \return \c 1 on success, \c 0 on failure
|
||||
*/
|
||||
int
|
||||
ped_alignment_is_aligned (const PedAlignment* align, const PedGeometry* geom,
|
||||
PedSector sector)
|
||||
{
|
||||
if (!align)
|
||||
return 0;
|
||||
|
||||
if (geom && !ped_geometry_test_sector_inside (geom, sector))
|
||||
return 0;
|
||||
|
||||
if (align->grain_size)
|
||||
return (sector - align->offset) % align->grain_size == 0;
|
||||
else
|
||||
return sector == align->offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
Reference in New Issue
Block a user