Restructure project, start importing sc3 code.
This commit is contained in:
108
include/scmp/geom/coord2d.h
Executable file
108
include/scmp/geom/coord2d.h
Executable file
@@ -0,0 +1,108 @@
|
||||
/// coord2d.h defines 2D point and polar coordinate systems.
|
||||
//
|
||||
// Project: scccl
|
||||
// File: include/math/coord2d.h
|
||||
// Author: Kyle Isom
|
||||
// Date: 2017-06-05
|
||||
// Namespace: math::geom
|
||||
//
|
||||
// coord2d.h defines 2D coordinate classes and functions.
|
||||
//
|
||||
// Copyright 2017 Kyle Isom <kyle@imap.cc>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef SCMATH_GEOM_COORD2D_H
|
||||
#define SCMATH_GEOM_COORD2D_H
|
||||
|
||||
#include <cmath>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace scmath {
|
||||
namespace geom {
|
||||
|
||||
|
||||
class Point2D;
|
||||
class Polar2D;
|
||||
|
||||
// Point2D is a logical grouping of a set of 2D cartesian coordinates.
|
||||
class Point2D {
|
||||
public:
|
||||
int x, y;
|
||||
|
||||
// A Point2D can be initialised by setting its members to 0, by providing the
|
||||
// x and y coordiantes, or through translation from a polar coordinate.
|
||||
Point2D() : x(0), y(0) {}
|
||||
Point2D(int _x, int _y) : x(_x), y(_y) {}
|
||||
Point2D(const Polar2D&);
|
||||
|
||||
std::string ToString(void);
|
||||
void ToPolar(Polar2D&);
|
||||
|
||||
// Rotate rotates the point by theta radians. Alternatively, a rotation
|
||||
// can use this point as the centre, with a polar coordinate and a rotation
|
||||
// amount (in radians). The latter is used to specify a central point
|
||||
// of rotation with vertices specified as polar coordinates from the centre.
|
||||
// Both forms take a reference to a Point2D to store the rotated point.
|
||||
void Rotate(Point2D& rotated, double theta);
|
||||
std::vector<Point2D> Rotate(std::vector<Polar2D>, double);
|
||||
|
||||
// Translate adds this point to the first argument, storing the result in the
|
||||
// second argument.
|
||||
void Translate(const Point2D& other, Point2D& translated);
|
||||
|
||||
// Distance returns the distance from this point to another.
|
||||
int Distance(const Point2D& other);
|
||||
|
||||
Point2D operator+(const Point2D &rhs) const { return Point2D(x + rhs.x, y + rhs.y); }
|
||||
Point2D operator-(const Point2D &rhs) const { return Point2D(x - rhs.x, y - rhs.y); }
|
||||
Point2D operator*(const int k) const { return Point2D(x * k, y * k); }
|
||||
bool operator==(const Point2D& rhs) const;
|
||||
bool operator!=(const Point2D& rhs) const { return !(*this == rhs); }
|
||||
friend std::ostream& operator<<(std::ostream& outs, const Point2D& pt);
|
||||
};
|
||||
|
||||
// A Polar2D is a 2D polar coordinate, specified in terms of the radius from
|
||||
// some origin and the angle from the positive X axis of a cartesian coordinate
|
||||
// system.
|
||||
class Polar2D {
|
||||
public:
|
||||
double r, theta;
|
||||
|
||||
// A Polar2D can be initialised as a zeroised polar coordinate, by specifying
|
||||
// the radius and angle directly, or via conversion from a Point2D.
|
||||
Polar2D() : r(0.0), theta(0.0) {}
|
||||
Polar2D(double _r, double _theta) : r(_r), theta(_theta) {}
|
||||
Polar2D(const Point2D&);
|
||||
|
||||
std::string ToString();
|
||||
void ToPoint(Point2D&);
|
||||
|
||||
// Rotate rotates the polar coordinate by the number of radians, storing the result
|
||||
// in the Polar2D argument.
|
||||
void Rotate(Polar2D&, double);
|
||||
|
||||
// RotateAround rotates this point about by theta radians, storing the rotated point
|
||||
// in result.
|
||||
void RotateAround(const Point2D& other, Point2D& result, double tjeta);
|
||||
|
||||
bool operator==(const Polar2D&) const;
|
||||
bool operator!=(const Polar2D& rhs) const { return !(*this == rhs); }
|
||||
friend std::ostream& operator<<(std::ostream&, const Polar2D&);
|
||||
};
|
||||
|
||||
|
||||
} // end namespace geom
|
||||
} // end namespace math
|
||||
#endif
|
||||
88
include/scmp/geom/orientation.h
Normal file
88
include/scmp/geom/orientation.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* orientation.h concerns itself with computing the orientation of some
|
||||
* vector with respect to a reference plane that is assumed to be the
|
||||
* of the Earth.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SCMATH_GEOM_ORIENTATION_H
|
||||
#define SCMATH_GEOM_ORIENTATION_H
|
||||
|
||||
|
||||
namespace scmath {
|
||||
namespace geom {
|
||||
|
||||
|
||||
/// \defgroup basis Basis vector indices.
|
||||
/// The following constants are provided as a convenience for indexing two-
|
||||
/// and three-dimensional vectors.
|
||||
|
||||
/// \ingroup basis
|
||||
/// Convenience constant for the x index.
|
||||
constexpr uint8_t Basis_x = 0;
|
||||
|
||||
/// \ingroup basis
|
||||
/// Convenience constant for the y index.
|
||||
constexpr uint8_t Basis_y = 1;
|
||||
|
||||
/// \ingroup basis
|
||||
/// Convenience constant for the z index.
|
||||
constexpr uint8_t Basis_z = 2;
|
||||
|
||||
|
||||
/// @brief Basis2d provides basis vectors for Vector2ds.
|
||||
static const Vector2d Basis2d[] = {
|
||||
Vector2d{1, 0},
|
||||
Vector2d{0, 1},
|
||||
};
|
||||
|
||||
|
||||
/// @brief Basis2d provides basis vectors for Vector2fs.
|
||||
static const Vector2f Basis2f[] = {
|
||||
Vector2f{1, 0},
|
||||
Vector2f{0, 1},
|
||||
};
|
||||
|
||||
|
||||
/// @brief Basis2d provides basis vectors for Vector3ds.
|
||||
static const Vector3d Basis3d[] = {
|
||||
Vector3d{1, 0, 0},
|
||||
Vector3d{0, 1, 0},
|
||||
Vector3d{0, 0, 1},
|
||||
};
|
||||
|
||||
|
||||
/// @brief Basis2d provides basis vectors for Vector3fs.
|
||||
static const Vector3f Basis3f[] = {
|
||||
Vector3f{1, 0, 0},
|
||||
Vector3f{0, 1, 0},
|
||||
Vector3f{0, 0, 1},
|
||||
};
|
||||
|
||||
|
||||
/// Heading2f returns a compass heading for a Vector2f.
|
||||
/// @param vec A vector orientation.
|
||||
/// @return The compass heading of the vector in radians.
|
||||
float Heading2f(Vector2f vec);
|
||||
|
||||
/// Heading2d returns a compass heading for a Vector2d.
|
||||
/// @param vec A vector orientation.
|
||||
/// @return The compass heading of the vector in radians.
|
||||
double Heading2d(Vector2d vec);
|
||||
|
||||
/// Heading3f returns a compass heading for a Vector2f.
|
||||
/// @param vec A vector orientation.
|
||||
/// @return The compass heading of the vector in radians.
|
||||
float Heading3f(Vector3f vec);
|
||||
|
||||
/// Heading3d returns a compass heading for a Vector2f.
|
||||
/// @param vec A vector orientation.
|
||||
/// @return The compass heading of the vector in radians.
|
||||
double Heading3d(Vector3d vec);
|
||||
|
||||
|
||||
} // namespace geom
|
||||
} // namespace math
|
||||
|
||||
|
||||
#endif // __WRMATH_ORIENTATION_H
|
||||
520
include/scmp/geom/quaternion.h
Normal file
520
include/scmp/geom/quaternion.h
Normal file
@@ -0,0 +1,520 @@
|
||||
/// quaternion.h contains an implementation of quaternions suitable
|
||||
/// for navigation in R3.
|
||||
#ifndef SCMATH_QUATERNION_H
|
||||
#define SCMATH_QUATERNION_H
|
||||
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
#include <scccl/math/math.h>
|
||||
#include <scccl/math/geom/vector.h>
|
||||
|
||||
/// math contains the shimmering clarity math library.
|
||||
namespace scmath {
|
||||
/// geom contains geometric classes and functions.
|
||||
namespace geom {
|
||||
|
||||
|
||||
/// @brief Quaternions provide a representation of orientation and rotations
|
||||
/// in three dimensions.
|
||||
///
|
||||
/// Quaternions encode rotations in three-dimensional space. While technically
|
||||
/// a quaternion is comprised of a real element and a complex vector<3>, for
|
||||
/// the purposes of this library, it is modeled as a floating point 4D vector
|
||||
/// of the form <w, x, y, z>, where x, y, and z represent an axis of rotation in
|
||||
/// R3 and w the angle, in radians, of the rotation about that axis. Where Euler
|
||||
/// angles are concerned, the ZYX (or yaw, pitch, roll) sequence is used.
|
||||
///
|
||||
/// For information on the underlying vector type, see the documentation for
|
||||
/// wr::geom::Vector.
|
||||
///
|
||||
/// The constructors are primarily intended for intended operations; in practice,
|
||||
/// the quaternionf() and quaterniond() functions are more useful for constructing
|
||||
/// quaternions from vectors and angles.
|
||||
///
|
||||
/// Like vectors, quaternions carry an internal tolerance value ε that is used for
|
||||
/// floating point comparisons. The math namespace contains the default values
|
||||
/// used for this; generally, a tolerance of 0.0001 is considered appropriate for
|
||||
/// the uses of this library. The tolerance can be explicitly set with the
|
||||
/// setEpsilon method.
|
||||
template<typename T>
|
||||
class Quaternion {
|
||||
public:
|
||||
/// The default Quaternion constructor returns an identity quaternion.
|
||||
Quaternion() : v(Vector<T, 3>{0.0, 0.0, 0.0}), w(1.0)
|
||||
{
|
||||
scmath::DefaultEpsilon(this->eps);
|
||||
v.setEpsilon(this->eps);
|
||||
};
|
||||
|
||||
|
||||
/// A Quaternion may be initialised with a Vector<T, 3> axis of rotation
|
||||
/// and an angle of rotation. This doesn't do the angle transforms to simplify
|
||||
/// internal operations.
|
||||
///
|
||||
/// @param _axis A three-dimensional vector of the same type as the Quaternion.
|
||||
/// @param _angle The angle of rotation about the axis of rotation.
|
||||
Quaternion(Vector<T, 3> _axis, T _angle) : v(_axis), w(_angle)
|
||||
{
|
||||
this->constrainAngle();
|
||||
scmath::DefaultEpsilon(this->eps);
|
||||
v.setEpsilon(this->eps);
|
||||
};
|
||||
|
||||
|
||||
/// A Quaternion may be initialised with a Vector<T, 4> comprised of
|
||||
/// the axis of rotation followed by the angle of rotation.
|
||||
///
|
||||
/// @param vector A vector in the form <w, x, y, z>.
|
||||
Quaternion(Vector<T, 4> vector) :
|
||||
v(Vector<T, 3>{vector[1], vector[2], vector[3]}),
|
||||
w(vector[0])
|
||||
{
|
||||
this->constrainAngle();
|
||||
scmath::DefaultEpsilon(this->eps);
|
||||
v.setEpsilon(this->eps);
|
||||
}
|
||||
|
||||
|
||||
/// A Quaternion may be constructed with an initializer list of
|
||||
/// type T, which must have exactly N elements.
|
||||
///
|
||||
/// @param ilst An initial set of values in the form <w, x, y, z>.
|
||||
Quaternion(std::initializer_list<T> ilst)
|
||||
{
|
||||
auto it = ilst.begin();
|
||||
|
||||
this->v = Vector<T, 3>{it[1], it[2], it[3]};
|
||||
this->w = it[0];
|
||||
|
||||
this->constrainAngle();
|
||||
scmath::DefaultEpsilon(this->eps);
|
||||
v.setEpsilon(this->eps);
|
||||
}
|
||||
|
||||
|
||||
/// Set the comparison tolerance for this quaternion.
|
||||
///
|
||||
/// @param epsilon A tolerance value.
|
||||
void
|
||||
setEpsilon(T epsilon)
|
||||
{
|
||||
this->eps = epsilon;
|
||||
this->v.setEpsilon(epsilon);
|
||||
}
|
||||
|
||||
|
||||
/// Return the axis of rotation of this quaternion.
|
||||
///
|
||||
/// @return The axis of rotation of this quaternion.
|
||||
Vector<T, 3>
|
||||
axis() const
|
||||
{
|
||||
return this->v;
|
||||
}
|
||||
|
||||
|
||||
/// Return the angle of rotation of this quaternion.
|
||||
///
|
||||
/// @return the angle of rotation of this quaternion.
|
||||
T
|
||||
angle() const
|
||||
{
|
||||
return this->w;
|
||||
}
|
||||
|
||||
|
||||
/// Compute the dot product of two quaternions.
|
||||
///
|
||||
/// \param other Another quaternion.
|
||||
/// \return The dot product between the two quaternions.
|
||||
T
|
||||
dot(const Quaternion<T> &other) const
|
||||
{
|
||||
double innerProduct = this->v[0] * other.v[0];
|
||||
|
||||
innerProduct += (this->v[1] * other.v[1]);
|
||||
innerProduct += (this->v[2] * other.v[2]);
|
||||
innerProduct += (this->w * other.w);
|
||||
return innerProduct;
|
||||
}
|
||||
|
||||
|
||||
/// Compute the norm of a quaternion. Treating the Quaternion as a
|
||||
/// Vector<T, 4>, it's the same as computing the magnitude.
|
||||
///
|
||||
/// @return A non-negative real number.
|
||||
T
|
||||
norm() const
|
||||
{
|
||||
T n = 0;
|
||||
|
||||
n += (this->v[0] * this->v[0]);
|
||||
n += (this->v[1] * this->v[1]);
|
||||
n += (this->v[2] * this->v[2]);
|
||||
n += (this->w * this->w);
|
||||
|
||||
return std::sqrt(n);
|
||||
}
|
||||
|
||||
|
||||
/// Return the unit quaternion.
|
||||
///
|
||||
/// \return The unit quaternion.
|
||||
Quaternion
|
||||
unitQuaternion()
|
||||
{
|
||||
return *this / this->norm();
|
||||
}
|
||||
|
||||
/// Compute the conjugate of a quaternion.
|
||||
///
|
||||
/// @return The conjugate of this quaternion.
|
||||
Quaternion
|
||||
conjugate() const
|
||||
{
|
||||
return Quaternion(Vector<T, 4>{this->w, -this->v[0], -this->v[1], -this->v[2]});
|
||||
}
|
||||
|
||||
|
||||
/// Compute the inverse of a quaternion.
|
||||
///
|
||||
/// @return The inverse of this quaternion.
|
||||
Quaternion
|
||||
inverse() const
|
||||
{
|
||||
T _norm = this->norm();
|
||||
|
||||
return this->conjugate() / (_norm * _norm);
|
||||
}
|
||||
|
||||
|
||||
/// Determine whether this is an identity quaternion.
|
||||
///
|
||||
/// \return true if this is an identity quaternion.
|
||||
bool
|
||||
isIdentity() const {
|
||||
return this->v.isZero() &&
|
||||
scmath::WithinTolerance(this->w, (T)1.0, this->eps);
|
||||
}
|
||||
|
||||
|
||||
/// Determine whether this is a unit quaternion.
|
||||
///
|
||||
/// @return true if this is a unit quaternion.
|
||||
bool
|
||||
isUnitQuaternion() const
|
||||
{
|
||||
return scmath::WithinTolerance(this->norm(), (T) 1.0, this->eps);
|
||||
}
|
||||
|
||||
|
||||
/// Return the quaternion as a Vector<T, 4>, with the axis of rotation
|
||||
/// followed by the angle of rotation.
|
||||
///
|
||||
/// @return A vector representation of the quaternion.
|
||||
Vector<T, 4>
|
||||
asVector() const
|
||||
{
|
||||
return Vector<T, 4>{this->w, this->v[0], this->v[1], this->v[2]};
|
||||
}
|
||||
|
||||
|
||||
/// Rotate vector vr about this quaternion.
|
||||
///
|
||||
/// @param vr The vector to be rotated.
|
||||
/// @return The rotated vector.
|
||||
Vector<T, 3>
|
||||
rotate(Vector<T, 3> vr) const
|
||||
{
|
||||
return (this->conjugate() * vr * (*this)).axis();
|
||||
}
|
||||
|
||||
|
||||
/// Return the Euler angles for this quaternion as a vector of
|
||||
/// <yaw, pitch, roll>. Users of this function should watch out
|
||||
/// for gimbal lock.
|
||||
///
|
||||
/// @return A vector<T, 3> containing <yaw, pitch, roll>
|
||||
Vector<T, 3>
|
||||
euler() const
|
||||
{
|
||||
T yaw, pitch, roll;
|
||||
T a = this->w, a2 = a * a;
|
||||
T b = this->v[0], b2 = b * b;
|
||||
T c = this->v[1], c2 = c * c;
|
||||
T d = this->v[2], d2 = d * d;
|
||||
|
||||
yaw = std::atan2(2 * ((a * b) + (c * d)), a2 - b2 - c2 + d2);
|
||||
pitch = std::asin(2 * ((b * d) - (a * c)));
|
||||
roll = std::atan2(2 * ((a * d) + (b * c)), a2 + b2 - c2 - d2);
|
||||
|
||||
return Vector<T, 3>{yaw, pitch, roll};
|
||||
}
|
||||
|
||||
|
||||
/// Perform quaternion addition with another quaternion.
|
||||
///
|
||||
/// @param other The quaternion to be added with this one.
|
||||
/// @return The result of adding the two quaternions together.
|
||||
Quaternion
|
||||
operator+(const Quaternion<T> &other) const
|
||||
{
|
||||
return Quaternion(this->v + other.v, this->w + other.w);
|
||||
}
|
||||
|
||||
|
||||
/// Perform quaternion subtraction with another quaternion.
|
||||
///
|
||||
/// @param other The quaternion to be subtracted from this one.
|
||||
/// @return The result of subtracting the other quaternion from this one.
|
||||
Quaternion
|
||||
operator-(const Quaternion<T> &other) const
|
||||
{
|
||||
return Quaternion(this->v - other.v, this->w - other.w);
|
||||
}
|
||||
|
||||
|
||||
/// Perform scalar multiplication.
|
||||
///
|
||||
/// @param k The scaling value.
|
||||
/// @return A scaled quaternion.
|
||||
Quaternion
|
||||
operator*(const T k) const
|
||||
{
|
||||
return Quaternion(this->v * k, this->w * k);
|
||||
}
|
||||
|
||||
|
||||
/// Perform scalar division.
|
||||
///
|
||||
/// @param k The scalar divisor.
|
||||
/// @return A scaled quaternion.
|
||||
Quaternion
|
||||
operator/(const T k) const
|
||||
{
|
||||
return Quaternion(this->v / k, this->w / k);
|
||||
}
|
||||
|
||||
|
||||
/// Perform quaternion Hamilton multiplication with a three-
|
||||
/// dimensional vector; this is done by treating the vector
|
||||
/// as a pure quaternion (e.g. with an angle of rotation of 0).
|
||||
///
|
||||
/// @param vector The vector to multiply with this quaternion.
|
||||
/// @return The Hamilton product of the quaternion and vector.
|
||||
Quaternion
|
||||
operator*(const Vector<T, 3> &vector) const
|
||||
{
|
||||
return Quaternion(vector * this->w + this->v.cross(vector),
|
||||
(T) 0.0);
|
||||
}
|
||||
|
||||
|
||||
/// Perform quaternion Hamilton multiplication.
|
||||
///
|
||||
/// @param other The other quaternion to multiply with this one.
|
||||
/// @result The Hamilton product of the two quaternions.
|
||||
Quaternion
|
||||
operator*(const Quaternion<T> &other) const
|
||||
{
|
||||
T angle = (this->w * other.w) -
|
||||
(this->v * other.v);
|
||||
Vector<T, 3> axis = (other.v * this->w) +
|
||||
(this->v * other.w) +
|
||||
(this->v.cross(other.v));
|
||||
return Quaternion(axis, angle);
|
||||
}
|
||||
|
||||
|
||||
/// Perform quaternion equality checking.
|
||||
/// @param other The quaternion to check equality against.
|
||||
/// @return True if the two quaternions are equal within their tolerance.
|
||||
bool
|
||||
operator==(const Quaternion<T> &other) const
|
||||
{
|
||||
return (this->v == other.v) &&
|
||||
(scmath::WithinTolerance(this->w, other.w, this->eps));
|
||||
}
|
||||
|
||||
|
||||
/// Perform quaternion inequality checking.
|
||||
///
|
||||
/// @param other The quaternion to check inequality against.
|
||||
/// @return True if the two quaternions are unequal within their tolerance.
|
||||
bool
|
||||
operator!=(const Quaternion<T> &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
/// Support stream output of a quaternion in the form `a + <i, j, k>`.
|
||||
/// \todo improve the formatting.
|
||||
///
|
||||
/// @param outs An output stream
|
||||
/// @param q A quaternion
|
||||
/// @return The output stream
|
||||
friend std::ostream &
|
||||
operator<<(std::ostream &outs, const Quaternion<T> &q)
|
||||
{
|
||||
outs << q.w << " + " << q.v;
|
||||
return outs;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr T minRotation = -4 * M_PI;
|
||||
static constexpr T maxRotation = 4 * M_PI;
|
||||
|
||||
Vector<T, 3> v; // axis of rotation
|
||||
T w; // angle of rotation
|
||||
T eps;
|
||||
|
||||
void
|
||||
constrainAngle()
|
||||
{
|
||||
if (this->w < 0.0) {
|
||||
this->w = std::fmod(this->w, this->minRotation);
|
||||
}
|
||||
else {
|
||||
this->w = std::fmod(this->w, this->maxRotation);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// \defgroup quaternion_aliases Quaternion type aliases.
|
||||
/// Type aliases are provided for float and double quaternions.
|
||||
///
|
||||
|
||||
/// \ingroup quaternion_aliases
|
||||
/// Type alias for a float Quaternion.
|
||||
typedef Quaternion<float> Quaternionf;
|
||||
|
||||
/// \ingroup quaternion_aliases
|
||||
/// Type alias for a double Quaternion.
|
||||
typedef Quaternion<double> Quaterniond;
|
||||
|
||||
|
||||
/// Return a float quaternion scaled appropriately from a vector and angle,
|
||||
/// e.g. angle = cos(angle / 2), axis.unitVector() * sin(angle / 2).
|
||||
///
|
||||
/// @param axis The axis of rotation.
|
||||
/// @param angle The angle of rotation.
|
||||
/// @return A quaternion.
|
||||
/// @relatesalso Quaternion
|
||||
Quaternionf quaternionf(Vector3f axis, float angle);
|
||||
|
||||
|
||||
/// Return a double quaternion scaled appropriately from a vector and angle,
|
||||
/// e.g. angle = cos(angle / 2), axis.unitVector() * sin(angle / 2).
|
||||
///
|
||||
/// @param axis The axis of rotation.
|
||||
/// @param angle The angle of rotation.
|
||||
/// @return A quaternion.
|
||||
/// @relatesalso Quaternion
|
||||
Quaterniond quaterniond(Vector3d axis, double angle);
|
||||
|
||||
|
||||
/// Return a double quaternion scaled appropriately from a vector and angle,
|
||||
/// e.g. angle = cos(angle / 2), axis.unitVector() * sin(angle / 2).
|
||||
///
|
||||
/// @param axis The axis of rotation.
|
||||
/// @param angle The angle of rotation.
|
||||
/// @return A quaternion.
|
||||
/// @relatesalso Quaternion
|
||||
template <typename T>
|
||||
Quaternion<T>
|
||||
quaternion(Vector<T, 3> axis, T angle)
|
||||
{
|
||||
return Quaternion<T>(axis.unitVector() * std::sin(angle / (T)2.0),
|
||||
std::cos(angle / (T)2.0));
|
||||
}
|
||||
|
||||
|
||||
/// Given a vector of Euler angles in ZYX sequence (e.g. yaw, pitch, roll),
|
||||
/// return a quaternion.
|
||||
///
|
||||
/// @param euler A vector Euler angle in ZYX sequence.
|
||||
/// @return A Quaternion representation of the orientation represented
|
||||
/// by the Euler angles.
|
||||
/// @relatesalso Quaternion
|
||||
Quaternionf quaternionf_from_euler(Vector3f euler);
|
||||
|
||||
|
||||
/// Given a vector of Euler angles in ZYX sequence (e.g. yaw, pitch, roll),
|
||||
/// return a quaternion.
|
||||
///
|
||||
/// @param euler A vector Euler angle in ZYX sequence.
|
||||
/// @return A Quaternion representation of the orientation represented
|
||||
/// by the Euler angles.
|
||||
/// @relatesalso Quaternion
|
||||
Quaterniond quaterniond_from_euler(Vector3d euler);
|
||||
|
||||
|
||||
/// LERP computes the linear interpolation of two quaternions at some
|
||||
/// fraction of the distance between them.
|
||||
///
|
||||
/// \tparam T
|
||||
/// \param p The starting quaternion.
|
||||
/// \param q The ending quaternion.
|
||||
/// \param t The fraction of the distance between the two quaternions to
|
||||
/// interpolate.
|
||||
/// \return A Quaternion representing the linear interpolation of the
|
||||
/// two quaternions.
|
||||
template <typename T>
|
||||
Quaternion<T>
|
||||
LERP(Quaternion<T> p, Quaternion<T> q, T t)
|
||||
{
|
||||
return (p + (q - p) * t).unitQuaternion();
|
||||
}
|
||||
|
||||
|
||||
/// ShortestSLERP computes the shortest distance spherical linear
|
||||
/// interpolation between two quaternions at some fraction of the
|
||||
/// distance between them.
|
||||
///
|
||||
/// \tparam T
|
||||
/// \param p The starting quaternion.
|
||||
/// \param q The ending quaternion.Short
|
||||
/// \param t The fraction of the distance between the two quaternions
|
||||
/// to interpolate.
|
||||
/// \return A Quaternion representing the shortest path between two
|
||||
/// quaternions.
|
||||
template <typename T>
|
||||
Quaternion<T>
|
||||
ShortestSLERP(Quaternion<T> p, Quaternion<T> q, T t)
|
||||
{
|
||||
assert(p.isUnitQuaternion());
|
||||
assert(q.isUnitQuaternion());
|
||||
|
||||
T dp = p.dot(q);
|
||||
T sign = dp < 0.0 ? -1.0 : 1.0;
|
||||
T omega = std::acos(dp * sign);
|
||||
T sin_omega = std::sin(omega); // Compute once.
|
||||
|
||||
if (dp > 0.99999) {
|
||||
return LERP(p, q * sign, t);
|
||||
}
|
||||
|
||||
return (p * std::sin((1.0 - t) * omega) / sin_omega) +
|
||||
(q * sign * std::sin(omega*t) / sin_omega);
|
||||
}
|
||||
|
||||
|
||||
/// Run a quick self test to exercise basic functionality of the Quaternion
|
||||
/// class to verify correct operation. Note that if \#NDEBUG is defined, the
|
||||
/// self test is disabled.
|
||||
void Quaternion_SelfTest();
|
||||
|
||||
|
||||
} // namespace geom
|
||||
} // namespace wr
|
||||
|
||||
|
||||
#endif // WRMATH_QUATERNION_H
|
||||
422
include/scmp/geom/vector.h
Normal file
422
include/scmp/geom/vector.h
Normal file
@@ -0,0 +1,422 @@
|
||||
//
|
||||
// Project: scccl
|
||||
// File: include/math/vectors.h
|
||||
// Author: Kyle Isom
|
||||
// Date: 2017-06-05
|
||||
// Namespace: math::vectors.
|
||||
//
|
||||
// vectors.h defines the Vector2D class and associated functions in the
|
||||
// namespace math::vectors.
|
||||
//
|
||||
// Copyright 2017 Kyle Isom <kyle@imap.cc>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef SCMATH_VECTORS_H
|
||||
#define SCMATH_VECTORS_H
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <initializer_list>
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
|
||||
#include <scccl/math/math.h>
|
||||
|
||||
|
||||
// This implementation is essentially a C++ translation of a Python library
|
||||
// I wrote for Coursera's "Linear Algebra for Machine Learning" course. Many
|
||||
// of the test vectors come from quiz questions in the class.
|
||||
|
||||
|
||||
namespace scmath {
|
||||
namespace geom {
|
||||
|
||||
|
||||
/// @brief Vectors represent a direction and magnitude.
|
||||
///
|
||||
/// Vector provides a standard interface for dimensionless fixed-size
|
||||
/// vectors. Once instantiated, they cannot be modified.
|
||||
///
|
||||
/// Note that while the class is templated, it's intended to be used with
|
||||
/// floating-point types.
|
||||
///
|
||||
/// Vectors can be indexed like arrays, and they contain an epsilon value
|
||||
/// that defines a tolerance for equality.
|
||||
template <typename T, size_t N>
|
||||
class Vector {
|
||||
public:
|
||||
/// The default constructor creates a unit vector for a given type
|
||||
/// and size.
|
||||
Vector()
|
||||
{
|
||||
T unitLength = (T)1.0 / std::sqrt(N);
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
this->arr[i] = unitLength;
|
||||
}
|
||||
|
||||
scmath::DefaultEpsilon(this->epsilon);
|
||||
}
|
||||
|
||||
|
||||
/// If given an initializer_list, the vector is created with
|
||||
/// those values. There must be exactly N elements in the list.
|
||||
/// @param ilst An intializer list with N elements of type T.
|
||||
Vector(std::initializer_list<T> ilst)
|
||||
{
|
||||
assert(ilst.size() == N);
|
||||
|
||||
scmath::DefaultEpsilon(this->epsilon);
|
||||
std::copy(ilst.begin(), ilst.end(), this->arr.begin());
|
||||
}
|
||||
|
||||
|
||||
/// Compute the length of the vector.
|
||||
/// @return The length of the vector.
|
||||
T magnitude() const {
|
||||
T result = 0;
|
||||
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
result += (this->arr[i] * this->arr[i]);
|
||||
}
|
||||
return std::sqrt(result);
|
||||
}
|
||||
|
||||
|
||||
/// Set the tolerance for equality checks. At a minimum, this allows
|
||||
/// for systemic errors in floating math arithmetic.
|
||||
/// @param eps is the maximum difference between this vector and
|
||||
/// another.
|
||||
void
|
||||
setEpsilon(T eps)
|
||||
{
|
||||
this->epsilon = eps;
|
||||
}
|
||||
|
||||
|
||||
/// Determine whether this is a zero vector.
|
||||
/// @return true if the vector is zero.
|
||||
bool
|
||||
isZero() const
|
||||
{
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
if (!scmath::WithinTolerance(this->arr[i], (T)0.0, this->epsilon)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// Obtain the unit vector for this vector.
|
||||
/// @return The unit vector
|
||||
Vector
|
||||
unitVector() const
|
||||
{
|
||||
return *this / this->magnitude();
|
||||
}
|
||||
|
||||
|
||||
/// Determine if this is a unit vector, e.g. if its length is 1.
|
||||
/// @return true if the vector is a unit vector.
|
||||
bool
|
||||
isUnitVector() const
|
||||
{
|
||||
return scmath::WithinTolerance(this->magnitude(), (T)1.0, this->epsilon);
|
||||
}
|
||||
|
||||
|
||||
/// Compute the angle between two other vectors.
|
||||
/// @param other Another vector.
|
||||
/// @return The angle in radians between the two vectors.
|
||||
T
|
||||
angle(const Vector<T, N> &other) const
|
||||
{
|
||||
Vector<T, N> unitA = this->unitVector();
|
||||
Vector<T, N> unitB = other.unitVector();
|
||||
|
||||
// Can't compute angles with a zero vector.
|
||||
assert(!this->isZero());
|
||||
assert(!other.isZero());
|
||||
return std::acos(unitA * unitB);
|
||||
}
|
||||
|
||||
|
||||
/// Determine whether two vectors are parallel.
|
||||
/// @param other Another vector
|
||||
/// @return True if the angle between the vectors is zero.
|
||||
bool
|
||||
isParallel(const Vector<T, N> &other) const
|
||||
{
|
||||
if (this->isZero() || other.isZero()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
T angle = this->angle(other);
|
||||
if (scmath::WithinTolerance(angle, (T)0.0, this->epsilon)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// Determine if two vectors are orthogonal or perpendicular to each
|
||||
/// other.
|
||||
/// @param other Another vector
|
||||
/// @return True if the two vectors are orthogonal.
|
||||
bool
|
||||
isOrthogonal(const Vector<T, N> &other) const
|
||||
{
|
||||
if (this->isZero() || other.isZero()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return scmath::WithinTolerance(*this * other, (T)0.0, this->epsilon);
|
||||
}
|
||||
|
||||
|
||||
/// Project this vector onto some basis vector.
|
||||
/// @param basis The basis vector to be projected onto.
|
||||
/// @return A vector that is the projection of this onto the basis
|
||||
/// vector.
|
||||
Vector
|
||||
projectParallel(const Vector<T, N> &basis) const
|
||||
{
|
||||
Vector<T, N> unit_basis = basis.unitVector();
|
||||
|
||||
return unit_basis * (*this * unit_basis);
|
||||
}
|
||||
|
||||
|
||||
/// Project this vector perpendicularly onto some basis vector.
|
||||
/// This is also called the rejection of the vector.
|
||||
/// @param basis The basis vector to be projected onto.
|
||||
/// @return A vector that is the orthogonal projection of this onto
|
||||
/// the basis vector.
|
||||
Vector
|
||||
projectOrthogonal(const Vector<T, N> &basis)
|
||||
{
|
||||
Vector<T, N> spar = this->projectParallel(basis);
|
||||
return *this - spar;
|
||||
}
|
||||
|
||||
|
||||
/// Compute the cross product of two vectors. This is only defined
|
||||
/// over three-dimensional vectors.
|
||||
/// @param other Another 3D vector.
|
||||
/// @return The cross product vector.
|
||||
Vector
|
||||
cross(const Vector<T, N> &other) const
|
||||
{
|
||||
assert(N == 3);
|
||||
return Vector<T, N> {
|
||||
(this->arr[1] * other.arr[2]) - (other.arr[1] * this->arr[2]),
|
||||
-((this->arr[0] * other.arr[2]) - (other.arr[0] * this->arr[2])),
|
||||
(this->arr[0] * other.arr[1]) - (other.arr[0] * this->arr[1])
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// Perform vector addition with another vector.
|
||||
/// @param other The vector to be added.
|
||||
/// @return A new vector that is the result of adding this and the
|
||||
/// other vector.
|
||||
Vector
|
||||
operator+(const Vector<T, N> &other) const
|
||||
{
|
||||
Vector<T, N> vec;
|
||||
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
vec.arr[i] = this->arr[i] + other.arr[i];
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
|
||||
/// Perform vector subtraction with another vector.
|
||||
/// @param other The vector to be subtracted from this vector.
|
||||
/// @return A new vector that is the result of subtracting the
|
||||
/// other vector from this one.
|
||||
Vector
|
||||
operator-(const Vector<T, N> &other) const
|
||||
{
|
||||
Vector<T, N> vec;
|
||||
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
vec.arr[i] = this->arr[i] - other.arr[i];
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
|
||||
/// Perform scalar multiplication of this vector by some scale factor.
|
||||
/// @param k The scaling value.
|
||||
/// @return A new vector that is this vector scaled by k.
|
||||
Vector
|
||||
operator*(const T k) const
|
||||
{
|
||||
Vector<T, N> vec;
|
||||
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
vec.arr[i] = this->arr[i] * k;
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
|
||||
/// Perform scalar division of this vector by some scale factor.
|
||||
/// @param k The scaling value
|
||||
/// @return A new vector that is this vector scaled by 1/k.
|
||||
Vector
|
||||
operator/(const T k) const
|
||||
{
|
||||
Vector<T, N> vec;
|
||||
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
vec.arr[i] = this->arr[i] / k;
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
|
||||
/// Compute the dot product between two vectors.
|
||||
/// @param other The other vector.
|
||||
/// @return A scalar value that is the dot product of the two vectors.
|
||||
T
|
||||
operator*(const Vector<T, N> &other) const
|
||||
{
|
||||
T result = 0;
|
||||
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
result += (this->arr[i] * other.arr[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// Compare two vectors for equality.
|
||||
/// @param other The other vector.
|
||||
/// @return Return true if all the components of both vectors are
|
||||
/// within the tolerance value.
|
||||
bool
|
||||
operator==(const Vector<T, N> &other) const
|
||||
{
|
||||
for (size_t i = 0; i<N; i++) {
|
||||
if (!scmath::WithinTolerance(this->arr[i], other.arr[i], this->epsilon)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// Compare two vectors for inequality.
|
||||
/// @param other The other vector.
|
||||
/// @return Return true if any of the components of both vectors are
|
||||
/// not within the tolerance value.
|
||||
bool
|
||||
operator!=(const Vector<T, N> &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
|
||||
/// Support array indexing into vector.
|
||||
///
|
||||
/// Note that the values of the vector cannot be modified. Instead,
|
||||
/// it's required to do something like the following:
|
||||
///
|
||||
/// ```
|
||||
/// Vector3d a {1.0, 2.0, 3.0};
|
||||
/// Vector3d b {a[0], a[1]*2.0, a[2]};
|
||||
/// ```
|
||||
///
|
||||
/// @param i The component index.
|
||||
/// @return The value of the vector component at i.
|
||||
const T&
|
||||
operator[](size_t i) const
|
||||
{
|
||||
return this->arr[i];
|
||||
}
|
||||
|
||||
|
||||
/// Support outputting vectors in the form "<i, j, ...>".
|
||||
/// @param outs An output stream.
|
||||
/// @param vec The vector to be formatted.
|
||||
/// @return The output stream.
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& outs, const Vector<T, N>& vec)
|
||||
{
|
||||
outs << "<";
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
outs << vec.arr[i];
|
||||
if (i < (N-1)) {
|
||||
outs << ", ";
|
||||
}
|
||||
}
|
||||
outs << ">";
|
||||
return outs;
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t dim = N;
|
||||
T epsilon;
|
||||
std::array<T, N> arr;
|
||||
};
|
||||
|
||||
///
|
||||
/// \defgroup vector_aliases Vector type aliases.
|
||||
///
|
||||
|
||||
/// \ingroup vector_aliases
|
||||
/// A number of shorthand aliases for vectors are provided. They follow
|
||||
/// the form of VectorNt, where N is the dimension and t is the type.
|
||||
/// For example, a 2D float vector is Vector2f.
|
||||
|
||||
/// \ingroup vector_aliases
|
||||
/// @brief Type alias for a two-dimensional float vector.
|
||||
typedef Vector<float, 2> Vector2f;
|
||||
|
||||
/// \ingroup vector_aliases
|
||||
/// @brief Type alias for a three-dimensional float vector.
|
||||
typedef Vector<float, 3> Vector3f;
|
||||
|
||||
/// \ingroup vector_aliases
|
||||
/// @brief Type alias for a four-dimensional float vector.
|
||||
typedef Vector<float, 4> Vector4f;
|
||||
|
||||
/// \ingroup vector_aliases
|
||||
/// @brief Type alias for a two-dimensional double vector.
|
||||
typedef Vector<double, 2> Vector2d;
|
||||
|
||||
/// \ingroup vector_aliases
|
||||
/// @brief Type alias for a three-dimensional double vector.
|
||||
typedef Vector<double, 3> Vector3d;
|
||||
|
||||
/// \ingroup vector_aliases
|
||||
/// @brief Type alias for a four-dimensional double vector.
|
||||
typedef Vector<double, 4> Vector4d;
|
||||
|
||||
|
||||
} // namespace geom
|
||||
} // namespace math
|
||||
|
||||
|
||||
|
||||
#endif // SCMATH_VECTORS_H_H
|
||||
77
include/scmp/math.h
Normal file
77
include/scmp/math.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/// math.h provides certain useful mathematical functions.
|
||||
|
||||
#ifndef SCCCL_MATH_H
|
||||
#define SCCCL_MATH_H
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
namespace scmath {
|
||||
|
||||
|
||||
// MAX_RADIAN is a precomputed 2 * M_PI, and MIN_RADIAN is -2 * M_PI.
|
||||
constexpr double MAX_RADIAN = 2 * M_PI;
|
||||
constexpr double MIN_RADIAN = -2 * M_PI;
|
||||
constexpr double POS_HALF_RADIAN = M_PI / 2;
|
||||
constexpr double NEG_HALF_RADIAN = -(M_PI / 2);
|
||||
|
||||
|
||||
/// Roll m die of n sides, returning a vector of the dice.
|
||||
std::vector<int> Die(int m, int n);
|
||||
/// Roll m die of n sides, returning the total of the die.
|
||||
int DieTotal(int m, int n);
|
||||
/// Roll m die of n sides, and take the total of the top k die.
|
||||
int BestDie(int k, int m, int n);
|
||||
|
||||
|
||||
/// Convert radians to degrees.
|
||||
/// @param rads the angle in radians
|
||||
/// @return the angle in degrees.
|
||||
float RadiansToDegreesF(float rads);
|
||||
|
||||
/// Convert radians to degrees.
|
||||
/// @param rads the angle in radians
|
||||
/// @return the angle in degrees.
|
||||
double RadiansToDegreesD(double rads);
|
||||
|
||||
/// Convert degrees to radians.
|
||||
/// @param degrees the angle in degrees
|
||||
/// @return the angle in radians.
|
||||
float DegreesToRadiansF(float degrees);
|
||||
|
||||
/// Convert degrees to radians.
|
||||
/// @param degrees the angle in degrees
|
||||
/// @return the angle in radians.
|
||||
double DegreesToRadiansD(double degrees);
|
||||
|
||||
/// RotateRadians rotates theta0 by theta1 radians, wrapping the result to
|
||||
/// MIN_RADIAN <= result <= MAX_RADIAN.
|
||||
double RotateRadians(double theta0, double theta1);
|
||||
|
||||
/// Get the default epsilon value.
|
||||
/// @param epsilon The variable to store the epsilon value in.
|
||||
void DefaultEpsilon(double &epsilon);
|
||||
|
||||
/// Get the default epsilon value.
|
||||
/// @param epsilon The variable to store the epsilon value in.
|
||||
void DefaultEpsilon(float &epsilon);
|
||||
|
||||
|
||||
/// Return whether the two values of type T are equal to within some tolerance.
|
||||
/// @tparam T The type of value
|
||||
/// @param a A value of type T used as the left-hand side of an equality check.
|
||||
/// @param b A value of type T used as the right-hand side of an equality check.
|
||||
/// @param epsilon The tolerance value.
|
||||
/// @return Whether the two values are "close enough" to be considered equal.
|
||||
template <typename T>
|
||||
static T
|
||||
WithinTolerance(T a, T b, T epsilon)
|
||||
{
|
||||
return std::abs(a - b) < epsilon;
|
||||
}
|
||||
|
||||
|
||||
} // namespace math
|
||||
|
||||
|
||||
#endif //SCCCL_MATH_H
|
||||
21
include/scmp/motion2d.h
Normal file
21
include/scmp/motion2d.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Created by Kyle Isom on 2/21/20.
|
||||
//
|
||||
|
||||
#ifndef SCCCL_MOTION2D_H
|
||||
#define SCCCL_MOTION2D_H
|
||||
|
||||
|
||||
#include <scccl/math/geom/vector.h>
|
||||
|
||||
namespace scphys {
|
||||
namespace basic {
|
||||
|
||||
|
||||
scmath::geom::Vector2d Acceleration(double speed, double heading);
|
||||
|
||||
|
||||
} // namespace basic
|
||||
} // namespace phsyics
|
||||
|
||||
#endif //SCCCL_MOTION2D_H
|
||||
232
include/scsl/Arena.h
Normal file
232
include/scsl/Arena.h
Normal file
@@ -0,0 +1,232 @@
|
||||
///
|
||||
/// \file Arena.h
|
||||
/// \author K. Isom <kyle@imap.cc>
|
||||
/// \date 2023-10-06
|
||||
/// \brief Memory management using an arena.
|
||||
///
|
||||
/// Arena defines a memory management backend for pre-allocating memory.
|
||||
///
|
||||
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||
///
|
||||
/// Permission to use, copy, modify, and/or distribute this software for
|
||||
/// any purpose with or without fee is hereby granted, provided that
|
||||
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
/// PERFORMANCE OF THIS SOFTWARE.
|
||||
///
|
||||
/// \section PLATFORM SUPPORT
|
||||
///
|
||||
/// Arena will build on the major platforms, but memory-mapped files are only
|
||||
/// supported on Unix-like systems. File I/O on Windows, for example, reads the
|
||||
/// file into an allocated arena. See Arena::Open for more details.
|
||||
|
||||
|
||||
#ifndef KIMODEM_ARENA_H
|
||||
#define KIMODEM_ARENA_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "Exceptions.h"
|
||||
|
||||
|
||||
#if defined(__WIN64__) || defined(__WIN32__) || defined(WIN32)
|
||||
|
||||
#include <Windows.h>
|
||||
#include <fileapi.h>
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace scsl {
|
||||
|
||||
|
||||
/// \enum ArenaType
|
||||
///
|
||||
/// ArenaType describes the type of \class Arena.
|
||||
enum class ArenaType
|
||||
: uint8_t {
|
||||
/// Uninit is an unintialized arena.
|
||||
Uninit,
|
||||
/// Static is an arena backed by a static block of memory.
|
||||
Static,
|
||||
/// Alloc is an arena backed by allocated memory.
|
||||
Alloc,
|
||||
/// MemoryMapped is an arena backed by a memory-mapped file.
|
||||
MemoryMapped,
|
||||
};
|
||||
|
||||
|
||||
/// Arena is the class that implements a memory arena.
|
||||
///
|
||||
/// The Arena uses the concept of a cursor to point to memory in the arena. The
|
||||
/// #Start and #End methods return pointers to the start and end of the
|
||||
/// arena memory.
|
||||
///
|
||||
/// The arena should be initialized with one of the Set methods (SetStatic,
|
||||
/// SetAlloc) or one of the file-based options (Create, Open, MemoryMap). At
|
||||
/// this point, no further memory management should be done until the end of the
|
||||
/// arena's life, at which point Destroy should be called.
|
||||
class Arena {
|
||||
public:
|
||||
/// An Arena is initialized with no backing memory.
|
||||
Arena();
|
||||
|
||||
~Arena();
|
||||
|
||||
/// SetStatic points the arena to a chunk of memory. That memory is
|
||||
/// intended to be statically allocated, e.g. via a global `static
|
||||
/// uint8_t memory[memSize];`. If the arena is already backed, then
|
||||
/// #Destroy will be called first.
|
||||
///
|
||||
/// \param mem A pointer to a section of memory, preferably statically
|
||||
/// allocated.
|
||||
/// \param memSize The size of the memory section.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
int SetStatic(uint8_t *mem, size_t memSize);
|
||||
|
||||
/// SetAlloc allocates a chunk of memory for the arena; the arena takes
|
||||
/// ownership. If the arena is already backed, then #Destroy will be
|
||||
/// called first.
|
||||
///
|
||||
/// \param allocSize The size of memory to allocate.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
int SetAlloc(size_t allocSize);
|
||||
|
||||
|
||||
/// MemoryMap points the arena to a memory-mapped file. This is
|
||||
/// currently only supported on Linux. If the arena is already backed,
|
||||
/// then #Destroy will be called first.
|
||||
///
|
||||
/// \param memFileDes File descriptor to map into memory.
|
||||
/// \param memSize The size of memory to map.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
#if defined(__posix__) || defined(__linux__) || defined(__APPLE__)
|
||||
int MemoryMap(int memFileDes, size_t memSize);
|
||||
#else
|
||||
|
||||
int MemoryMap(int memFileDes, size_t memSize)
|
||||
{ (void)memFileDes; (void)memSize; throw NotImplemented("WIN32"); }
|
||||
|
||||
#endif
|
||||
/// Create creates a new file, truncating it if it already exists. On
|
||||
/// Unix-based platforms, the arena will be backed by a memory via
|
||||
/// #MemoryMap. On other platforms (e.g. Windows), the arena will read
|
||||
/// the file into an allocated arena, calling #SetAlloc.
|
||||
///
|
||||
/// \param path The path to the file that should be created.
|
||||
/// \param fileSize The size of the file to create.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
int Create(const char *path, size_t fileSize);
|
||||
|
||||
/// Open reads a file into the arena; the file must already exist. On
|
||||
/// Unix-based platforms, the arena will be backed by a memory via
|
||||
/// #MemoryMap. On other platforms (e.g. Windows), the arena will read
|
||||
/// the file into an allocated arena, calling #SetAlloc. On these
|
||||
/// platforms, in order to persist changes, #Write must be called to
|
||||
/// sync changes to disk.
|
||||
///
|
||||
/// \param path The path to the file to be loaded.
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
int Open(const char *path);
|
||||
|
||||
/// Start returns a pointer to the start of the memory in the arena.
|
||||
///
|
||||
/// \return A pointer to the start of the arena memory.
|
||||
uint8_t *Start() const
|
||||
{ return this->store; }
|
||||
|
||||
/// End returns a pointer to the end of the arena memory.
|
||||
///
|
||||
/// \return A pointer to the end of the arena memory.
|
||||
uint8_t *End()
|
||||
{ return this->store + this->size; }
|
||||
|
||||
/// CursorInArena checks whether the cursor is still in the arena.
|
||||
///
|
||||
/// \param cursor A pointer that ostensibly points to the arena's
|
||||
/// memory.
|
||||
/// \return True if the cursor is still in the arena.
|
||||
bool CursorInArena(const uint8_t *cursor);
|
||||
|
||||
/// Returns the current size of the arena.
|
||||
///
|
||||
/// \return The size of the arena.
|
||||
size_t Size() const
|
||||
{ return this->size; }
|
||||
|
||||
/// Type returns an ArenaType describing the arena.
|
||||
///
|
||||
/// \return An ArenaType describing the backing memory for the arena.
|
||||
ArenaType Type() const
|
||||
{ return this->arenaType; }
|
||||
|
||||
/// Ready returns whether the arena is initialized.
|
||||
bool Ready() const
|
||||
{ return this->Type() != ArenaType::Uninit; };
|
||||
|
||||
/// Clear zeroizes the memory in the arena.
|
||||
void Clear();
|
||||
|
||||
/// Destroy removes any backing memory (e.g. from SetAlloc or
|
||||
/// MemoryMap). This does not call Clear; if the arena was backed by a
|
||||
/// file that should be persisted, it would wipe out the file.
|
||||
void Destroy();
|
||||
|
||||
/// Write dumps the arena to a file suitable for loading by Open.
|
||||
///
|
||||
/// \warning DANGER: if arena is memory-mapped, DO NOT WRITE TO THE
|
||||
/// BACKING FILE!
|
||||
///
|
||||
/// \param path
|
||||
/// \return Returns 0 on success and -1 on error.
|
||||
int Write(const char *path);
|
||||
|
||||
/// This operator allows the data in the arena to be accessed
|
||||
/// as if it were an array. If the index is out of bounds, it
|
||||
/// will throw a range_error.
|
||||
///
|
||||
/// \throws std::range_error.
|
||||
///
|
||||
/// \param index The index to retrieve.
|
||||
/// \return
|
||||
uint8_t &operator[](size_t index);
|
||||
|
||||
private:
|
||||
uint8_t *store;
|
||||
size_t size;
|
||||
int fd;
|
||||
ArenaType arenaType;
|
||||
};
|
||||
|
||||
|
||||
/// Write an Arena out to the output stream.
|
||||
///
|
||||
/// The resulting output looks something like
|
||||
/// ```
|
||||
/// Arena<allocated>@0x7fff91dfad70,store<128B>@0x0055d6c5881ec0.
|
||||
/// ^ ^ ^ ^
|
||||
/// | +- base memory | +- store memory
|
||||
/// +- arena type +- arena size
|
||||
/// ```
|
||||
///
|
||||
/// \param os
|
||||
/// \param arena
|
||||
/// \return
|
||||
std::ostream &operator<<(std::ostream &os, Arena &arena);
|
||||
|
||||
|
||||
} // namespace scsl
|
||||
|
||||
|
||||
#endif
|
||||
232
include/scsl/Buffer.h
Normal file
232
include/scsl/Buffer.h
Normal file
@@ -0,0 +1,232 @@
|
||||
///
|
||||
/// \file Buffer.h
|
||||
/// \author K. Isom <kyle@imap.cc>
|
||||
/// \date 2023-10-09
|
||||
/// \brief Buffer implements basic line buffers.
|
||||
///
|
||||
/// Buffer implements a basic uint8_t line buffer that is intended for use in text
|
||||
/// editing. It allocates memory in powers of two, and will grow or shrink
|
||||
/// as needed.
|
||||
///
|
||||
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||
///
|
||||
/// Permission to use, copy, modify, and/or distribute this software for
|
||||
/// any purpose with or without fee is hereby granted, provided that
|
||||
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
/// PERFORMANCE OF THIS SOFTWARE.
|
||||
///
|
||||
|
||||
#ifndef KGE_BUFFER_H
|
||||
#define KGE_BUFFER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
namespace scsl {
|
||||
|
||||
/// Buffer is a basic line buffer.
|
||||
///
|
||||
/// The buffer manages its own internal memory, growing and shrinking
|
||||
/// as needed. Its capacity is separate from its length; the optimal
|
||||
/// capacity is determined as the nearest power of two that is greater
|
||||
/// than the length of the buffer. For example, if the buffer has a
|
||||
/// length of 5 bytes, 8 bytes will be allocated. If the buffer is 9
|
||||
/// bytes, 16 bytes will be allocated.
|
||||
///
|
||||
/// The #Append and #Insert methods will call #Resize as necessary to grow
|
||||
/// the buffer. Similarly the #Remove methods will call #Trim to reclaim some
|
||||
/// memory if possible, but only if #AutoTrimIsEnabled (it is by default).
|
||||
class Buffer {
|
||||
public:
|
||||
/// A Buffer can be constructed empty, with no memory allocated (yet).
|
||||
Buffer();
|
||||
|
||||
/// A Buffer can be constructed with an explicit capacity.
|
||||
///
|
||||
/// \param initialCapacity The initial allocation size for the buffer.
|
||||
explicit Buffer(size_t initialCapacity);
|
||||
|
||||
/// A Buffer can be initialized with a starting C-style string.
|
||||
explicit Buffer(const char *s);
|
||||
|
||||
/// A Buffer can be initialized with a starting string.
|
||||
explicit Buffer(const std::string s);
|
||||
|
||||
~Buffer()
|
||||
{ this->Reclaim(); }
|
||||
|
||||
/// Contents returns the Buffer's contents.
|
||||
uint8_t *Contents() const
|
||||
{ return this->contents; }
|
||||
|
||||
/// Length returns the length of the data currently stored in the
|
||||
/// buffer.
|
||||
size_t Length() const
|
||||
{ return this->length; };
|
||||
|
||||
/// Capacity returns the amount of memory allocated to the Buffer.
|
||||
size_t Capacity() const
|
||||
{ return this->capacity; }
|
||||
|
||||
/// Append copies in a C-style string to the end of the buffer.
|
||||
///
|
||||
/// \param s The string to append.
|
||||
/// \return True if the Buffer was resized.
|
||||
bool Append(const char *s);
|
||||
|
||||
/// Append copies in a string to the end of the buffer.
|
||||
///
|
||||
/// \param s The string to append.
|
||||
/// \return True if the Buffer was resized.
|
||||
bool Append(const std::string s);
|
||||
|
||||
/// Append copies in a byte buffer to the end of the buffer.
|
||||
///
|
||||
/// \param data The byte buffer to insert.
|
||||
/// \param datalen The length of the byte buffer.
|
||||
/// \return True if the Buffer was resized.
|
||||
bool Append(const uint8_t *data, const size_t datalen);
|
||||
|
||||
/// Append copies a single character to the end of the buffer.
|
||||
///
|
||||
/// \param c The character to append.
|
||||
/// \return True if the Buffer was resized.
|
||||
bool Append(const uint8_t c);
|
||||
|
||||
/// Insert copies a C-style string into the buffer at index.
|
||||
///
|
||||
/// \param index The index to insert the string at.
|
||||
/// \param s The string to insert.
|
||||
/// \return True if the Buffer was resized.
|
||||
bool Insert(const size_t index, const char *s);
|
||||
|
||||
/// Insert copies a string into the buffer at index.
|
||||
///
|
||||
/// \param index The index the string should be inserted at.
|
||||
/// \param s The string to insert.
|
||||
/// \return True if the Buffer was resized.
|
||||
bool Insert(const size_t index, const std::string s);
|
||||
|
||||
/// Insert copies a uint8_t buffer into the buffer at index.
|
||||
///
|
||||
/// \param index The index to insert the buffer at.
|
||||
/// \param data The buffer to insert.
|
||||
/// \param datalen The size of the data buffer.
|
||||
/// \return True if the Buffer was resized.
|
||||
bool
|
||||
Insert(const size_t index, const uint8_t *data, const size_t datalen);
|
||||
|
||||
/// Insert copies a character into the buffer at index.
|
||||
///
|
||||
/// \param index The index to insert the character at.
|
||||
/// \param c The character to insert.
|
||||
/// \return True if the Buffer was resized.
|
||||
bool Insert(const size_t index, const uint8_t c);
|
||||
|
||||
/// Remove removes `count` bytes from the buffer at `index`.
|
||||
///
|
||||
/// \param index The starting index to remove bytes from.
|
||||
/// \param count The number of bytes to remove.
|
||||
/// \return True if the Buffer was resized.
|
||||
bool Remove(const size_t index, const size_t count);
|
||||
|
||||
/// Remove removes a single byte from the buffer.
|
||||
///
|
||||
/// \param index The index pointing to the byte to be removed.
|
||||
/// \return True if the Buffer was resized.
|
||||
bool Remove(size_t index); // remove single char
|
||||
|
||||
/* memory management */
|
||||
|
||||
/// Resize changes the capacity of the buffer to `newCapacity`.
|
||||
///
|
||||
/// If newCapacity is less than the length of the Buffer, it
|
||||
/// will remove enough bytes from the end to make this happen.
|
||||
///
|
||||
/// \param newCapacity The new capacity for the Buffer.
|
||||
void Resize(size_t newCapacity);
|
||||
|
||||
/// Trim will resize the Buffer to an appropriate size based on
|
||||
/// its length.
|
||||
///
|
||||
/// \return The new capacity of the Buffer.
|
||||
size_t Trim();
|
||||
|
||||
/// DisableAutoTrim prevents the #Buffer from automatically
|
||||
/// trimming memory after a call to #Remove.
|
||||
void DisableAutoTrim()
|
||||
{ this->autoTrim = false; }
|
||||
|
||||
/// EnableAutoTrim enables automatically trimming memory after
|
||||
/// calls to #Remove.
|
||||
void EnableAutoTrim()
|
||||
{ this->autoTrim = true; }
|
||||
|
||||
/// AutoTrimIsEnabled returns true if autotrim is enabled.
|
||||
///
|
||||
/// \return #Remove will call Trim.
|
||||
bool AutoTrimIsEnabled()
|
||||
{ return this->autoTrim; }
|
||||
|
||||
/// Clear removes the data stored in the buffer. It will not
|
||||
/// call #Trim; the capacity of the buffer will not be altered.
|
||||
void Clear();
|
||||
|
||||
/// Reclaim the memory in the buffer: the buffer will call #Clear,
|
||||
/// followed by deleting any allocated memory.
|
||||
void Reclaim();
|
||||
|
||||
/// HexDump dumps the data in the buffer to the output stream;
|
||||
/// it is intended as a debugging tool.
|
||||
///
|
||||
/// \param os The output stream to write to.
|
||||
void HexDump(std::ostream &os);
|
||||
|
||||
/// This operator allows the data in the buffer to be accessed
|
||||
/// as if it were an array. If the index is out of bounds, it
|
||||
/// will throw a range_error.
|
||||
///
|
||||
/// \throws std::range_error.
|
||||
///
|
||||
/// \param index The index to retrieve.
|
||||
/// \return
|
||||
uint8_t &operator[](size_t index);
|
||||
|
||||
/// Two buffers are equal if their lengths are the same and
|
||||
/// their contents are the same. Equality is irrespective of
|
||||
/// their capacities.
|
||||
friend bool operator==(const Buffer &lhs, const Buffer &rhs);
|
||||
|
||||
private:
|
||||
size_t mustGrow(size_t delta) const;
|
||||
|
||||
bool shiftRight(size_t offset, size_t delta);
|
||||
|
||||
bool shiftLeft(size_t offset, size_t delta);
|
||||
|
||||
uint8_t *contents;
|
||||
size_t length;
|
||||
size_t capacity;
|
||||
bool autoTrim;
|
||||
};
|
||||
|
||||
/// The << operator is overloaded to write out the contents of the Buffer.
|
||||
std::ostream &operator<<(std::ostream &os, const Buffer &buf);
|
||||
|
||||
/// Two Buffers are not equal if their lengths differ or if their contents
|
||||
/// differ.
|
||||
inline bool operator!=(const Buffer &lhs, const Buffer &rhs) { return !(lhs == rhs); };
|
||||
|
||||
} // namespace scsl
|
||||
|
||||
|
||||
#endif // KGE_BUFFER_H
|
||||
127
include/scsl/Commander.h
Normal file
127
include/scsl/Commander.h
Normal file
@@ -0,0 +1,127 @@
|
||||
///
|
||||
/// \file Commander.h
|
||||
/// \author K. Isom <kyle@imap.cc>
|
||||
/// \date 2023-10-10
|
||||
/// \brief Subprogram tooling.
|
||||
///
|
||||
/// Commander is tooling for creating subcommand interfaces for command-line
|
||||
/// programs. For an example, see phonebook.cc.
|
||||
///
|
||||
/// The basic idea is to enable writing programs of the form
|
||||
/// ```
|
||||
/// $ some_tool subcommand args...
|
||||
/// ```
|
||||
///
|
||||
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||
///
|
||||
/// Permission to use, copy, modify, and/or distribute this software for
|
||||
/// any purpose with or without fee is hereby granted, provided that
|
||||
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
/// PERFORMANCE OF THIS SOFTWARE.
|
||||
///
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#ifndef SCSL_COMMANDER_H
|
||||
#define SCSL_COMMANDER_H
|
||||
|
||||
namespace scsl {
|
||||
|
||||
|
||||
/// CommanderFunc describes a function that can be run in Commander.
|
||||
///
|
||||
/// It expects an argument count and a list of arguments.
|
||||
using CommanderFunc = std::function<bool (int, char **)>;
|
||||
|
||||
|
||||
/// Subcommands are the individual commands for the program. A Subcommand
|
||||
/// will check that it has enough arguments before running its function.
|
||||
class Subcommand {
|
||||
public:
|
||||
/// Status describes the results of running a Subcommand.
|
||||
enum class Status : int8_t {
|
||||
/// The subcommand executed correctly.
|
||||
OK = 0,
|
||||
/// Not enough arguments were supplied to the subcommand.
|
||||
NotEnoughArgs = 1,
|
||||
/// The subcommand failed to run correctly.
|
||||
Failed = 2,
|
||||
/// The subcommand hasn't been registered in a Commander
|
||||
/// instance.
|
||||
CommandNotRegistered = 3,
|
||||
};
|
||||
|
||||
/// A Subcommand is initialized with a name, the number of arguments
|
||||
/// it requires, and a function to run.
|
||||
///
|
||||
/// \param name The subcommand name; this is the name that will select this command.
|
||||
/// \param argc The minimum number of arguments required by this subcommand.
|
||||
/// \param func A valid CommanderFunc.
|
||||
Subcommand(std::string name, int argc, CommanderFunc func)
|
||||
: fn(func), args(argc), command(name)
|
||||
{}
|
||||
|
||||
/// Name returns the name of this subcommand.
|
||||
std::string Name() { return this->command; }
|
||||
|
||||
/// Run attempts to run the CommanderFunc for this subcommand.
|
||||
///
|
||||
/// \param argc The number of arguments supplied.
|
||||
/// \param argv The argument list.
|
||||
/// \return A Status type indicating the status of running the command.
|
||||
Status Run(int argc, char **argv);
|
||||
private:
|
||||
CommanderFunc fn;
|
||||
int args;
|
||||
std::string command;
|
||||
};
|
||||
|
||||
/// Commander collects subcommands and can run the apppropriate one.
|
||||
///
|
||||
/// For example:
|
||||
/// ```
|
||||
/// auto command = string(argv[optind++]);
|
||||
/// Commander commander;
|
||||
///
|
||||
/// commander.Register(Subcommand("list", 0, listFiles));
|
||||
/// commander.Register(Subcommand("new", 1, newPhonebook));
|
||||
/// commander.Register(Subcommand("del", 1, delKey));
|
||||
/// commander.Register(Subcommand("has", 1, hasKey));
|
||||
/// commander.Register(Subcommand("get", 1, getKey));
|
||||
/// commander.Register(Subcommand("put", 2, putKey));
|
||||
///
|
||||
/// auto result = commander.Run(command, argc-optind, argv+optind);
|
||||
/// ```
|
||||
///
|
||||
class Commander {
|
||||
public:
|
||||
/// A Commander is initialized empty.
|
||||
Commander();
|
||||
|
||||
/// Register adds the subcommand. It will be copied into the Commander.
|
||||
bool Register(Subcommand scmd);
|
||||
|
||||
/// Try to run a subcommand registered with this Commander.
|
||||
Subcommand::Status Run(std::string command, int argc, char **argv);
|
||||
private:
|
||||
std::map<std::string, Subcommand *> cmap;
|
||||
};
|
||||
|
||||
|
||||
} // namespace scsl
|
||||
|
||||
|
||||
#endif //SCSL_COMMANDER_H
|
||||
142
include/scsl/Dictionary.h
Normal file
142
include/scsl/Dictionary.h
Normal file
@@ -0,0 +1,142 @@
|
||||
///
|
||||
/// \file Dictionary.h
|
||||
/// \author kyle (kyle@imap.cc)
|
||||
/// \date 2023-10-12
|
||||
/// \brief Key-value store built on top of Arena and TLV.
|
||||
///
|
||||
///
|
||||
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||
///
|
||||
/// Permission to use, copy, modify, and/or distribute this software for
|
||||
/// any purpose with or without fee is hereby granted, provided that
|
||||
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
/// PERFORMANCE OF THIS SOFTWARE.
|
||||
///
|
||||
|
||||
|
||||
#ifndef SCSL_DICTIONARY_H
|
||||
#define SCSL_DICTIONARY_H
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Arena.h"
|
||||
#include "TLV.h"
|
||||
|
||||
|
||||
static constexpr uint8_t DICTIONARY_TAG_KEY = 1;
|
||||
static constexpr uint8_t DICTIONARY_TAG_VAL = 2;
|
||||
|
||||
|
||||
namespace scsl {
|
||||
|
||||
|
||||
/*
|
||||
* A Dictionary is a collection of key-value pairs, similar to how
|
||||
* a dictionary is a mapping of names to definitions.
|
||||
*/
|
||||
/// Dictionary implements a key-value store on top of Arena and TLV::Record.
|
||||
///
|
||||
/// Keys and vales are stored as sequential pairs of TLV records; they are
|
||||
/// expected to contain string values but this isn't necessarily the case. The
|
||||
/// tag values default to a tag of DICTIONARY_TAG_KEY, and values to a tag of
|
||||
/// DICTIONARY_TAG_VAL.
|
||||
class Dictionary {
|
||||
public:
|
||||
/// A Dictionary can be initialized with just a backing Arena.
|
||||
///
|
||||
/// \param arena The backing arena for the Dictionary.
|
||||
Dictionary(Arena &arena) :
|
||||
arena(arena),
|
||||
kTag(DICTIONARY_TAG_KEY),
|
||||
vTag(DICTIONARY_TAG_VAL)
|
||||
{};
|
||||
|
||||
/// A Dictionary can also be configured with custom key and value types.
|
||||
///
|
||||
/// \param arena The backing arena for the Dictionary.
|
||||
/// \param kt The value to use for key tags.
|
||||
/// \param vt The value to use for val tags.
|
||||
Dictionary(Arena &arena, uint8_t kt, uint8_t vt) :
|
||||
arena(arena),
|
||||
kTag(kt),
|
||||
vTag(vt)
|
||||
{};
|
||||
|
||||
/// Lookup checks to see if the Dictionary has a value under key.
|
||||
///
|
||||
/// \param key The key to search for.
|
||||
/// \param klen The length of the key.
|
||||
/// \param res The TLV::Record to store the value in;
|
||||
/// \return True if the key was found, false otherwise.
|
||||
bool Lookup(const char *key, uint8_t klen, TLV::Record &res);
|
||||
|
||||
/// Set adds a pairing for key → value in the Dictionary.
|
||||
///
|
||||
/// If the key is already present in the dictionary, both the
|
||||
/// key and value are deleted, and a new pair is insert.
|
||||
///
|
||||
/// \warning If the key is present, but there isn't enough space to
|
||||
/// store the new Val, the Dictionary will not contain either key
|
||||
/// or value.
|
||||
///
|
||||
///
|
||||
/// \param key The key to associate.
|
||||
/// \param klen The length of the key.
|
||||
/// \param val The value to associate.
|
||||
/// \param vlen The length of the value.
|
||||
/// \return Returns 0 on success and -1 on failure.
|
||||
int Set(const char *key, uint8_t klen, const char *val,
|
||||
uint8_t vlen);
|
||||
|
||||
/// Contains checks the dictionary to see if it contains a given key.
|
||||
///
|
||||
/// \param key The key to look up.
|
||||
/// \param klen The length of the key.
|
||||
/// \return True if the key is in the Dictionary, otherwise false.
|
||||
bool Contains(const char *key, uint8_t klen);
|
||||
|
||||
/// Delete removes the key from the Dictionary.
|
||||
///
|
||||
/// \param key The key to look up.
|
||||
/// \param klen The length of the key.
|
||||
/// \return True if the key was removed, otherwise false.
|
||||
bool Delete(const char *key, uint8_t klen);
|
||||
|
||||
|
||||
/// DumpToFile is a wrapper aorund a call to Arena::Write on the
|
||||
/// underlying Arena.
|
||||
///
|
||||
/// \param path The path to the dumped file.
|
||||
/// \return 0 on success, -1 on failure.
|
||||
int DumpToFile(const char *path);
|
||||
|
||||
/// operator<< writes the key pairs phonto the output stream.
|
||||
///
|
||||
/// \param os The output stream to write to.
|
||||
/// \param dictionary The dictionary to write out.
|
||||
/// \return The output stream is returned.
|
||||
friend std::ostream &operator<<(std::ostream &os,
|
||||
const Dictionary &dictionary);
|
||||
private:
|
||||
uint8_t *seek(const char *key, uint8_t klen);
|
||||
|
||||
bool spaceAvailable(uint8_t klen, uint8_t vlen);
|
||||
|
||||
Arena &arena;
|
||||
uint8_t kTag;
|
||||
uint8_t vTag;
|
||||
};
|
||||
|
||||
|
||||
} // namespace scsl
|
||||
|
||||
#endif
|
||||
69
include/scsl/Exceptions.h
Normal file
69
include/scsl/Exceptions.h
Normal file
@@ -0,0 +1,69 @@
|
||||
///
|
||||
/// \file Exceptions.h
|
||||
/// \author K. Isom <kyle@imap.cc>
|
||||
/// \date 2023-10-10
|
||||
/// \brief Custom exceptions for use in SCSL used in writing test programs.
|
||||
///
|
||||
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||
///
|
||||
/// Permission to use, copy, modify, and/or distribute this software for
|
||||
/// any purpose with or without fee is hereby granted, provided that
|
||||
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
/// PERFORMANCE OF THIS SOFTWARE.
|
||||
///
|
||||
#ifndef SCSL_EXCEPTIONS_H
|
||||
#define SCSL_EXCEPTIONS_H
|
||||
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace scsl {
|
||||
|
||||
|
||||
/// NotImplemented is an exception reserved for unsupported platforms.
|
||||
///
|
||||
/// It is used to mark functionality included for compatibility, and useful for
|
||||
/// debugging.
|
||||
class NotImplemented : public std::exception {
|
||||
public:
|
||||
/// NotImplemented exceptions are constructed with a platform name.
|
||||
explicit NotImplemented(const char *pl) : platform((char *)pl) {}
|
||||
|
||||
/// what returns a message naming the platform.
|
||||
const char *what() const throw() {
|
||||
return this->platform;
|
||||
}
|
||||
private:
|
||||
char *platform;
|
||||
};
|
||||
|
||||
|
||||
/// AssertionFailed indicates that some invariant didn't hold.
|
||||
class AssertionFailed : public std::exception {
|
||||
public:
|
||||
/// AssertionFailed is constructed with a message describing what
|
||||
/// failed.
|
||||
explicit AssertionFailed(std::string message);
|
||||
|
||||
/// what returns a message describing the exception.
|
||||
const char *what() const throw();
|
||||
|
||||
private:
|
||||
std::string msg;
|
||||
};
|
||||
|
||||
|
||||
} // namespace scsl
|
||||
|
||||
|
||||
#endif //SCSL_EXCEPTIONS_H
|
||||
341
include/scsl/Flag.h
Normal file
341
include/scsl/Flag.h
Normal file
@@ -0,0 +1,341 @@
|
||||
///
|
||||
/// \file Flag.h
|
||||
/// \author K. Isom <kyle@imap.cc>
|
||||
/// \date 2023-10-12
|
||||
/// \brief Flag declares a command-line flag parser.
|
||||
///
|
||||
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||
///
|
||||
/// Permission to use, copy, modify, and/or distribute this software for
|
||||
/// any purpose with or without fee is hereby granted, provided that
|
||||
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
/// PERFORMANCE OF THIS SOFTWARE.
|
||||
///
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace scsl {
|
||||
|
||||
|
||||
/// FlagType indicates the value held in a FlagValue.
|
||||
///
|
||||
/// \todo When C++17 support is more common, switch to `std::variant`.
|
||||
enum class FlagType : uint8_t {
|
||||
Unknown = 0, ///< Unsupported value type.
|
||||
Boolean = 1, ///< bool
|
||||
Integer = 2, ///< int32_t
|
||||
UnsignedInteger = 3, ///< uint32_t
|
||||
SizeT = 4, ///< size_t
|
||||
String = 5, ///< std::string
|
||||
};
|
||||
|
||||
|
||||
/// FlagValue holds the value of a command line flag.
|
||||
typedef union {
|
||||
unsigned int u; ///< FlagType::UnsignedInteger
|
||||
int i; ///< FlagType::Integer
|
||||
std::size_t size; ///< FlagType::SizeT
|
||||
std::string *s; ///< FlagType::String
|
||||
bool b; ///< FlagType::Boolean
|
||||
} FlagValue;
|
||||
|
||||
|
||||
/// Flag describes an individual command-line flag.
|
||||
typedef struct {
|
||||
FlagType Type; ///< The type of the value in the flag.
|
||||
bool WasSet; ///< The flag was set on the command-line.
|
||||
std::string Name; ///< The name of the flag.
|
||||
std::string Description; ///< A description of the flag.
|
||||
FlagValue Value; ///< The flag's value.
|
||||
} Flag;
|
||||
|
||||
/// NewFlag is a helper function for constructing a new flag.
|
||||
///
|
||||
/// \param fName The name of the flag.
|
||||
/// \param fType The type of the flag.
|
||||
/// \param fDescription A description of the flag.
|
||||
/// \return A pointer to a flag.
|
||||
Flag *NewFlag(std::string fName, FlagType fType, std::string fDescription);
|
||||
|
||||
/// Flags provides a basic facility for processing command line flags.
|
||||
///
|
||||
/// Any remaining arguments after the args are added to the parser as
|
||||
/// arguments that can be accessed with NumArgs, Args, and Arg.
|
||||
///
|
||||
/// \note The parser automatically handles the -h and --help flags by
|
||||
/// calling Usage(std::cout, 0). The user can override this flag
|
||||
/// and handle providing help on their own.
|
||||
///
|
||||
/// \details
|
||||
///
|
||||
/// Arguments are named with their leading dash, e.g. "-f". For example,
|
||||
///
|
||||
/// ```c++
|
||||
/// flags.Register("-f", FlagType::String, "Path to a configuration file.");
|
||||
/// ```
|
||||
/// \section Usage
|
||||
///
|
||||
/// A short example program is below:
|
||||
///
|
||||
/// ```c++
|
||||
/// int
|
||||
/// main(int argc, char *argv[])
|
||||
/// {
|
||||
/// std::string server = "service.example.com";
|
||||
/// unsigned int port = 1234;
|
||||
///
|
||||
/// auto flags = new scsl::Flags("example-client",
|
||||
/// "This interacts with the example.com service.");
|
||||
/// flags->Register("-p", port, "server port");
|
||||
/// flags->Register("-s", server, "hostname to connect to");
|
||||
///
|
||||
/// auto status = flags->Parse(argc, argv);
|
||||
/// if (status != ParseStatus::OK) {
|
||||
/// std::cerr << "failed to parse flags: "
|
||||
/// << scsl::Flags::ParseStatusToString(status)
|
||||
/// << "\n";
|
||||
/// exit(1);
|
||||
/// }
|
||||
///
|
||||
/// auto wasSet = flags->GetString("-s", server);
|
||||
/// if (wasSet) {
|
||||
/// std::cout << "hostname override: " << server << "\n";
|
||||
/// }
|
||||
///
|
||||
/// wasSet = flags->GetUnsignedInteger("-p", port);
|
||||
/// if (wasSet) {
|
||||
/// std::cout << "port override: " << port << "\n";
|
||||
/// }
|
||||
///
|
||||
/// std::cout << "connecting to " << server << ":" << port << "\n";
|
||||
/// for (size_t i = 0; i < flags.NumArgs(); i++) {
|
||||
/// std::cout << "\tExecuting command " << flags.Arg(i) << "\n";
|
||||
/// }
|
||||
/// return 0;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
class Flags {
|
||||
public:
|
||||
/// ParseStatus describes the result of parsing command-line
|
||||
/// arguments.
|
||||
enum class ParseStatus : uint8_t {
|
||||
/// An unknown parsing error occurred. This is a bug,
|
||||
/// and users should never see this.
|
||||
Unknown = 0,
|
||||
|
||||
/// Parsing succeeded.
|
||||
OK = 1,
|
||||
|
||||
/// This is an internal status marking the end of
|
||||
/// command line flags.
|
||||
EndOfFlags = 2,
|
||||
|
||||
/// The command line flag provided isn't registered.
|
||||
NotRegistered = 3,
|
||||
|
||||
/// Not enough arguments were provided to a flag that
|
||||
/// takes an argument. For example, if "-p" expects
|
||||
/// a number, and the program was called with just
|
||||
/// `./program -p`, this error will be reported.
|
||||
NotEnoughArgs = 4,
|
||||
};
|
||||
|
||||
/// ParseStatusToString returns a string message describing the
|
||||
/// result of parsing command line args.
|
||||
static std::string ParseStatusToString(ParseStatus status);
|
||||
|
||||
/// Create a new flags parser for the named program.
|
||||
///
|
||||
/// \param fName The program name,
|
||||
Flags(std::string fName);
|
||||
|
||||
/// Create a new flags parser for the named program.
|
||||
///
|
||||
/// \param fName The program name.
|
||||
/// \param fDescription A short description of the program.
|
||||
Flags(std::string fName, std::string fDescription);
|
||||
|
||||
~Flags();
|
||||
|
||||
/// Register a new command line flag.
|
||||
///
|
||||
/// \param fName The name of the flag, including a leading dash.
|
||||
/// \param fType The type of the argument to parse.
|
||||
/// \param fDescription A description of the flag.
|
||||
/// \return True if the flag was registered, false if the flag could
|
||||
/// not be registered (e.g. a duplicate flag was registered).
|
||||
bool Register(std::string fName,
|
||||
FlagType fType,
|
||||
std::string fDescription);
|
||||
|
||||
/// Register a new boolean command line flag with a default value.
|
||||
///
|
||||
/// \note For booleans, it only makes sense to pass a false default
|
||||
/// value, as there is no way to set a boolean flag to false.
|
||||
/// This form is provided for compatibility with the other
|
||||
/// variants on this method.
|
||||
///
|
||||
/// \param fName The name of the flag, including a leading dash.
|
||||
/// \param defaultValue The default value for the flag.
|
||||
/// \param fDescription A short description of the flag.
|
||||
/// \return True if the flag was registered, false if the flag could
|
||||
/// not be registered (e.g. a duplicate flag was registered).
|
||||
bool Register(std::string fName,
|
||||
bool defaultValue,
|
||||
std::string fDescription);
|
||||
|
||||
/// Register a new integer command line flag with a default value.
|
||||
///
|
||||
/// \param fName The name of the flag, including a leading dash.
|
||||
/// \param defaultValue The default value for the flag.
|
||||
/// \param fDescription A short description of the flag.
|
||||
/// \return True if the flag was registered, false if the flag could
|
||||
/// not be registered (e.g. a duplicate flag was registered).
|
||||
bool Register(std::string fName,
|
||||
int defaultValue,
|
||||
std::string fDescription);
|
||||
|
||||
/// Register a new unsigned integer command line flag with a default
|
||||
/// value.
|
||||
///
|
||||
/// \param fName The name of the flag, including a leading dash.
|
||||
/// \param defaultValue The default value for the flag.
|
||||
/// \param fDescription A short description of the flag.
|
||||
/// \return True if the flag was registered, false if the flag could
|
||||
/// not be registered (e.g. a duplicate flag was registered).
|
||||
bool Register(std::string fName,
|
||||
unsigned int defaultValue,
|
||||
std::string fDescription);
|
||||
|
||||
/// Register a new size_t command line flag with a default value.
|
||||
///
|
||||
/// \param fName The name of the flag, including a leading dash.
|
||||
/// \param defaultValue The default value for the flag.
|
||||
/// \param fDescription A short description of the flag.
|
||||
/// \return True if the flag was registered, false if the flag could
|
||||
/// not be registered (e.g. a duplicate flag was registered).
|
||||
bool Register(std::string fName,
|
||||
size_t defaultValue,
|
||||
std::string fDescription);
|
||||
|
||||
/// Register a new string command line flag with a default value.
|
||||
///
|
||||
/// \param fName The name of the flag, including a leading dash.
|
||||
/// \param defaultValue The default value for the flag.
|
||||
/// \param fDescription A short description of the flag.
|
||||
/// \return True if the flag was registered, false if the flag could
|
||||
/// not be registered (e.g. a duplicate flag was registered).
|
||||
bool Register(std::string fName,
|
||||
std::string defaultValue,
|
||||
std::string fDescription);
|
||||
|
||||
/// Return the number of registered flags.
|
||||
size_t Size();
|
||||
|
||||
/// Lookup a flag.
|
||||
///
|
||||
/// \param fName The flag name.
|
||||
/// \return A pointer to flag if registered, or nullptr if the flag
|
||||
/// wasn't registered.
|
||||
Flag *Lookup(std::string fName);
|
||||
|
||||
/// ValueOf returns the value of the flag in the
|
||||
bool ValueOf(std::string fName, FlagValue &value);
|
||||
|
||||
/// Process a list of arguments into flags.
|
||||
///
|
||||
/// \param argc The number of arguments.
|
||||
/// \param argv The arguments passed to the program.
|
||||
/// \param skipFirst Flags expects to receive arguments directly
|
||||
/// from those passed to `main`, and defaults to skipping
|
||||
/// the first argument. Set to false if this is not the
|
||||
/// case.
|
||||
/// \return
|
||||
ParseStatus Parse(int argc, char **argv, bool skipFirst=true);
|
||||
|
||||
/// Print a usage message to the output stream.
|
||||
void Usage(std::ostream &os, int exitCode);
|
||||
|
||||
/// Return the number of arguments processed. These are any
|
||||
/// remaining elements in argv that are not flags.
|
||||
size_t NumArgs();
|
||||
|
||||
/// Return the arguments as a vector.
|
||||
std::vector<std::string> Args();
|
||||
|
||||
/// Return a particular argument.
|
||||
///
|
||||
/// \param index The argument number to extract.
|
||||
/// \return The argument at index i. If the index is greater than
|
||||
/// the number of arguments present, an out_of_range
|
||||
/// exception is thrown.
|
||||
std::string Arg(size_t index);
|
||||
|
||||
/// Retrieve the state of a boolean flag.
|
||||
///
|
||||
/// \param fName The flag to look up.
|
||||
/// \param flagValue The value to store.
|
||||
/// \return True if the value was set, or false if the value isn't
|
||||
/// a boolean or if it wasn't set.
|
||||
bool GetBool(std::string fName, bool &flagValue);
|
||||
|
||||
/// Retrieve the value of an unsigned integer flag.
|
||||
///
|
||||
/// \param fName The flag to look up.
|
||||
/// \param flagValue The value to store.
|
||||
/// \return True if the value was set, or false if the value isn't
|
||||
/// an unsigned integer or if it wasn't set.
|
||||
bool GetUnsignedInteger(std::string fName, unsigned int &flagValue);
|
||||
|
||||
/// Retrieve the value of an integer flag.
|
||||
///
|
||||
/// \param fName The flag to look up.
|
||||
/// \param flagValue The value to store.
|
||||
/// \return True if the value was set, or false if the value isn't
|
||||
/// an integer or if it wasn't set.
|
||||
bool GetInteger(std::string fName, int &flagValue);
|
||||
|
||||
/// Retrieve the value of a string flag.
|
||||
///
|
||||
/// \param fName The flag to look up.
|
||||
/// \param flagValue The value to store.
|
||||
/// \return True if the value was set, or false if the value isn't
|
||||
/// a string or if it wasn't set.
|
||||
bool GetString(std::string fName, std::string &flagValue);
|
||||
|
||||
/// Retrieve the value of a size_t flag.
|
||||
///
|
||||
/// \param fName The flag to look up.
|
||||
/// \param flagValue The value to store.
|
||||
/// \return True if the value was set, or false if the value isn't
|
||||
/// a size_t or if it wasn't set.
|
||||
bool GetSizeT(std::string fName, std::size_t &flagValue);
|
||||
|
||||
private:
|
||||
ParseStatus parseArg(int argc, char **argv, int &index);
|
||||
Flag *checkGetArg(std::string fName, FlagType eType);
|
||||
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::vector<std::string> args;
|
||||
std::map<std::string, Flag *> flags;
|
||||
};
|
||||
|
||||
|
||||
} // namespace scsl
|
||||
135
include/scsl/StringUtil.h
Normal file
135
include/scsl/StringUtil.h
Normal file
@@ -0,0 +1,135 @@
|
||||
///
|
||||
/// \file StringUtil.h
|
||||
/// \author K. Isom <kyle@imap.cc>
|
||||
/// \date 2023-10-14
|
||||
/// \brief Utilities for working with strings.
|
||||
///
|
||||
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||
///
|
||||
/// Permission to use, copy, modify, and/or distribute this software for
|
||||
/// any purpose with or without fee is hereby granted, provided that the
|
||||
/// above copyright notice and this permission notice appear in all copies.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
/// BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
|
||||
/// OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
/// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
/// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
/// SOFTWARE.
|
||||
///
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
#ifndef STRINGUTIL_H
|
||||
#define STRINGUTIL_H
|
||||
|
||||
|
||||
namespace scsl {
|
||||
|
||||
/// namespace U contains utilities.
|
||||
namespace U {
|
||||
|
||||
/// namespace S contains string-related functions.
|
||||
namespace S {
|
||||
|
||||
|
||||
/// Remove any whitespace at the beginning of the string. The string
|
||||
/// is modified in-place.
|
||||
void TrimLeadingWhitespace(std::string &s);
|
||||
|
||||
/// Remove any whitespace at the end of the string. The string is
|
||||
/// modified in-place.
|
||||
void TrimTrailingWhitespace(std::string &s);
|
||||
|
||||
/// Remove any whitespace at the beginning and end of the string. The
|
||||
/// string is modified in-place.
|
||||
void TrimWhitespace(std::string &s);
|
||||
|
||||
/// Remove any whitespace at the beginning of the string. The original
|
||||
/// string isn't modified, and a copy is returned.
|
||||
std::string TrimLeadingWhitespaceDup(std::string s);
|
||||
|
||||
/// Remove any whitespace at the end of the string. The original string
|
||||
/// isn't modified, and a copy is returned.
|
||||
std::string TrimTrailingWhitespaceDup(std::string s);
|
||||
|
||||
/// Remove any whitespace at the beginning and end of the string. The
|
||||
/// original string isn't modified, and a copy is returned.
|
||||
std::string TrimWhitespaceDup(std::string s);
|
||||
|
||||
|
||||
/// Split a line into key and value pairs. If the delimiter isn't found,
|
||||
/// the line is returned as the first element in the pair, and the second
|
||||
/// element will be empty.
|
||||
///
|
||||
/// \param line A string representing a line in a file.
|
||||
/// \param delimiter The string delimiter between the key and value.
|
||||
/// \return The key and value, or {line, ""}.
|
||||
std::vector<std::string> SplitKeyValuePair(std::string line, std::string delimiter);
|
||||
|
||||
/// Split a line into key and value pairs. If the delimiter isn't found,
|
||||
/// the line is returned as the first element in the pair, and the second
|
||||
/// element will be empty.
|
||||
///
|
||||
/// \param line A string representing a line in a file.
|
||||
/// \param delimiter The character delimiter between the key and value.
|
||||
/// \return The key and value.
|
||||
std::vector<std::string> SplitKeyValuePair(std::string line, char delimiter);
|
||||
|
||||
/// Split a string into parts based on the delimiter.
|
||||
///
|
||||
/// \param s The string that should be split.
|
||||
/// \param delimiter The string that delimits the parts of the string.
|
||||
/// \param maxCount The maximum number of parts to split. If 0, there is no
|
||||
/// limit to the number of parts.
|
||||
/// \return A vector containing all the parts of the string.
|
||||
std::vector<std::string> SplitN(std::string, std::string delimiter, size_t maxCount=0);
|
||||
|
||||
/// WrapText is a very simple wrapping function that breaks the line into
|
||||
/// lines of at most lineLength characters. It does this by breaking the
|
||||
/// line into individual words (splitting on whitespace).
|
||||
std::vector<std::string> WrapText(std::string line, size_t lineLength);
|
||||
|
||||
/// Write out a vector of lines indented with tabs.
|
||||
///
|
||||
/// \param os The output stream to write to.
|
||||
/// \param lines The lines of text to write.
|
||||
/// \param tabStop The number of tabs to indent.
|
||||
/// \param indentFirst Whether the first line should be indented.
|
||||
void WriteTabIndented(std::ostream &os, std::vector<std::string> lines,
|
||||
int tabStop, bool indentFirst);
|
||||
|
||||
/// Wrap a line, then output it to a stream.
|
||||
///
|
||||
/// \param os The output stream to write to.
|
||||
/// \param line The line to wrap and output.
|
||||
/// \param maxLength The maximum length of each section of text.
|
||||
/// \param tabStop The number of tabs to indent.
|
||||
/// \param indentFirst Whether the first line should be indented.
|
||||
void WriteTabIndented(std::ostream &os, std::string line, size_t maxLength,
|
||||
int tabStop, bool indentFirst);
|
||||
|
||||
|
||||
/// Write a string vector to the output stream in the same format as
|
||||
/// VectorToString.
|
||||
std::ostream &VectorToString(std::ostream &os, const std::vector<std::string> &svec);
|
||||
|
||||
/// Return a string represention of a string vector in the form
|
||||
/// [size]{"foo", "bar", ...}.
|
||||
std::string VectorToString(const std::vector<std::string> &svec);
|
||||
|
||||
|
||||
} // namespace S
|
||||
} // namespace U
|
||||
} // namespace scsl
|
||||
|
||||
|
||||
#endif // STRINGUTIL_H
|
||||
|
||||
|
||||
130
include/scsl/TLV.h
Normal file
130
include/scsl/TLV.h
Normal file
@@ -0,0 +1,130 @@
|
||||
///
|
||||
/// \file TLV.h
|
||||
/// \author K. Isom <kyle@imap.cc>
|
||||
/// \date 2023-10-06
|
||||
/// \brief TLV.h implements basic tag-length-value records.
|
||||
///
|
||||
/// TLV implements tag-length-value (TLV) records. Each record can have
|
||||
/// a maximum length of 253 bytes; each TLV record occupies a fixed 255
|
||||
/// bytes in memory. TLV records don't allocate memory.
|
||||
///
|
||||
/// This system uses an Arena as a backing store.
|
||||
///
|
||||
|
||||
#ifndef KIMODEM_TLV_H
|
||||
#define KIMODEM_TLV_H
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#include "Arena.h"
|
||||
|
||||
|
||||
namespace scsl {
|
||||
namespace TLV {
|
||||
|
||||
|
||||
#ifndef TLV_MAX_LEN
|
||||
static constexpr size_t TLV_MAX_LEN = 253;
|
||||
#endif
|
||||
|
||||
static constexpr uint8_t TAG_EMPTY = 0;
|
||||
|
||||
|
||||
/// Record describes a tag-length-value record.
|
||||
///
|
||||
/// TLV records occupy a fixed size in memory, which can be controlled with the
|
||||
/// TLV_MAX_LEN define. If this isn't defined, it defaults to a size of 253.
|
||||
/// When writen to an Arena, it occupies Len + 2 bytes. The strings
|
||||
/// are not null-terminated in the arena.
|
||||
struct Record {
|
||||
/// A Tag is used to identify the type of this record.
|
||||
uint8_t Tag;
|
||||
/// Len describes the number of bytes stored in #Val.
|
||||
uint8_t Len;
|
||||
/// Val contains the data in the record.
|
||||
uint8_t Val[TLV_MAX_LEN];
|
||||
};
|
||||
|
||||
/// WriteToMemory writes the TLV record into the arena at the location pointed
|
||||
/// to in the arena.
|
||||
///
|
||||
/// \param arena The backing memory store.
|
||||
/// \param cursor Pointer into the arena's memory.
|
||||
/// \param rec A TLV record to be serialized.
|
||||
/// \return A pointer the memory after the record.
|
||||
uint8_t *WriteToMemory(Arena &arena, uint8_t *cursor, Record &rec);
|
||||
|
||||
/// ReadFromMemory reads a record from the memory pointed to by the cursor.
|
||||
///
|
||||
/// \param rec The TLV record to be filled in.
|
||||
/// \param cursor A pointer into an arena's memory store.
|
||||
void ReadFromMemory(Record &rec, uint8_t *cursor);
|
||||
|
||||
/// SetRecord sets a record.
|
||||
///
|
||||
/// \param rec The record to be set.
|
||||
/// \param tag The record's tag.
|
||||
/// \param length The record's length.
|
||||
/// \param data The data to fill the record with.
|
||||
void SetRecord(Record &rec, uint8_t tag, uint8_t length, const char *data);
|
||||
|
||||
/// DeleteRecord removes the record from the arena. All records ahead of this
|
||||
/// record are shifted backwards so that there are no gaps.
|
||||
void DeleteRecord(Arena &arena, uint8_t *cursor);
|
||||
|
||||
/*
|
||||
* returns a pointer to memory where the record was found,
|
||||
* e.g. LocateTag(...)[0] is the tag of the found record.
|
||||
* FindTag will call LocateTag and then SkipRecord if the
|
||||
* tag was found.
|
||||
*/
|
||||
/// FindTag finds the next occurrence of the record's tag.
|
||||
///
|
||||
/// The record must have a tag set, which tells FindTag which tag to look for.
|
||||
/// If found, it fills the record. \see LocateTag.
|
||||
///
|
||||
/// \param arena The backing memory for the TLV store.
|
||||
/// \param cursor A pointer to memory inside the arena; if it's NULL, the
|
||||
/// search starts at the beginning of the arena.
|
||||
/// \param rec The record to be filled.
|
||||
/// \return If the tag is found, a cursor pointing to the next record is
|
||||
/// returned; otherwise nullptr is returned.
|
||||
uint8_t *FindTag(Arena &arena, uint8_t *cursor, Record &rec);
|
||||
|
||||
/// LocateTag operates similarly to FindTag, but the cursor points to the
|
||||
/// beginning of the found record.
|
||||
///
|
||||
/// \param arena The backing memory for the TLV store.
|
||||
/// \param cursor A pointer to memory inside the arena; if it's NULL, the
|
||||
/// search starts at the beginning of the arena.
|
||||
/// \param rec The record to be filled.
|
||||
/// \return If the tag is found, a cursor pointing to the record is
|
||||
/// returned; otherwise nullptr is returned.
|
||||
uint8_t *LocateTag(Arena &arena, uint8_t *cursor, Record &rec);
|
||||
|
||||
/// FindEmpty finds a pointer the next available empty space.
|
||||
///
|
||||
/// \return A cursor to the start of empty space in the arena, or nullptr
|
||||
/// if there is no more empty space available.
|
||||
///
|
||||
/// \param arena The backing memory for the TLV store.
|
||||
/// \param cursor A pointer to memory inside the arena; if it's NULL, the
|
||||
/// search starts at the beginning of the arena.
|
||||
/// \return If the arena has space available, a cursor pointing the start
|
||||
/// of empty space; otherwise, nullptr is returned.
|
||||
uint8_t *FindEmpty(Arena &arena, uint8_t *cursor);
|
||||
|
||||
/// SkipRecord skips the cursor to the next record.
|
||||
///
|
||||
/// \param rec The record that should be skipped.
|
||||
/// \param cursor A pointer to the record in the arena.
|
||||
/// \return The pointer to the next record in the arena.
|
||||
uint8_t *SkipRecord(Record &rec, uint8_t *cursor);
|
||||
|
||||
|
||||
} // namespace TLV
|
||||
} // namespace scsl
|
||||
|
||||
|
||||
#endif
|
||||
80
include/scsl/scsl.h
Normal file
80
include/scsl/scsl.h
Normal file
@@ -0,0 +1,80 @@
|
||||
///
|
||||
/// \file scsl.h
|
||||
/// \author kyle (kyle@imap.cc)
|
||||
/// \date 2023-10-10
|
||||
/// \brief scsl is my collection of C++ data structures and code.
|
||||
///
|
||||
/// scsl.h is a utility header that includes all of SCSL.
|
||||
///
|
||||
/// \section COPYRIGHT
|
||||
///
|
||||
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||
///
|
||||
/// Permission to use, copy, modify, and/or distribute this software for
|
||||
/// any purpose with or without fee is hereby granted, provided that the
|
||||
/// above copyright notice and this permission notice appear in all copies.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
/// BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
|
||||
/// OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
/// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
/// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
/// SOFTWARE.
|
||||
///
|
||||
|
||||
#ifndef SCSL_SCSL_H
|
||||
#define SCSL_SCSL_H
|
||||
|
||||
|
||||
#include <scsl/Arena.h>
|
||||
#include <scsl/Buffer.h>
|
||||
#include <scsl/Commander.h>
|
||||
#include <scsl/Dictionary.h>
|
||||
#include <scsl/Exceptions.h>
|
||||
#include <scsl/Flag.h>
|
||||
#include <scsl/StringUtil.h>
|
||||
#include <scsl/TLV.h>
|
||||
#include <scsl/Test.h>
|
||||
|
||||
|
||||
/// scsl is the top-level namespace containing all the code in this library.
|
||||
namespace scsl {
|
||||
|
||||
/// \mainpage scsl documentation
|
||||
///
|
||||
/// \section Introduction
|
||||
///
|
||||
/// This is a collection of data structures and subroutines that I find
|
||||
/// useful in building things.
|
||||
///
|
||||
/// This library arose from two main use cases.
|
||||
///
|
||||
/// \subsection kimodem The modem
|
||||
///
|
||||
/// On the one hand, I was building a wireless modem for some Z80 computers I
|
||||
/// have. I needed to be able to store a phonebook of SSIDs and WPA keys, as
|
||||
/// well as short names to host:port descriptors. I had a limited amount of
|
||||
/// persistent NVRAM storage and no SD card or other removeable media, so
|
||||
/// typical desktop-oriented serialization mechanisms weren't going to really
|
||||
/// work well. Furthermore, when working with microcontrollers, I prefer not to
|
||||
/// dynamically allocate memory as much as possible. This led to building out
|
||||
/// Arena, TLV::Record to store the records, and finally Dictionary to make use
|
||||
/// of both of them.
|
||||
///
|
||||
/// Closely related to this, I've been working on building an ARM-based handheld
|
||||
/// computer, for which I would also need a memory arena.
|
||||
///
|
||||
/// \subsection textedit The text editors
|
||||
///
|
||||
/// Some time ago, I wrote a console text editor of my own; then later, started
|
||||
/// working on a graphical editor. For this, I needed some data structures to
|
||||
/// manage memory in the editor. Thus, Buffer was born.
|
||||
///
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // SCSL_SCSL_H
|
||||
60
include/sctest/Assert.h
Normal file
60
include/sctest/Assert.h
Normal file
@@ -0,0 +1,60 @@
|
||||
///
|
||||
/// \file Test.h
|
||||
/// \author K. Isom <kyle@imap.cc>
|
||||
/// \date 2023-10-09
|
||||
/// \brief Tooling to assist in building test programs..
|
||||
///
|
||||
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||
///
|
||||
/// Permission to use, copy, modify, and/or distribute this software for
|
||||
/// any purpose with or without fee is hereby granted, provided that
|
||||
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
/// PERFORMANCE OF THIS SOFTWARE.
|
||||
///
|
||||
|
||||
#ifndef SCSL_TEST_H
|
||||
#define SCSL_TEST_H
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace scsl {
|
||||
|
||||
|
||||
/// TestAssert is a variant on the assert macro. This variant is intended to be
|
||||
/// a drop-in replacement for the cassert macro: even in release mode, the tests
|
||||
/// should still run.
|
||||
///
|
||||
/// If NDEBUG is set, TestAssert will throw an exception if condition is false.
|
||||
/// Otherwise, it calls assert after printing the message.
|
||||
///
|
||||
/// \param condition If true, TestAssert throws an exception.
|
||||
void TestAssert(bool condition);
|
||||
|
||||
|
||||
/// TestAssert is a variant on the assert macro.
|
||||
///
|
||||
/// If NDEBUG is set, TestAssert will throw an exception if condition is false.
|
||||
/// Otherwise, it calls assert after printing the message.
|
||||
///
|
||||
/// In addition to NDEBUG, SCSL_NOEXCEPT will suppress assertions.
|
||||
///
|
||||
/// \throws AssertionFailed
|
||||
///
|
||||
/// \param condition The condition to assert.
|
||||
/// \param message The message that should be displayed if condition is false.
|
||||
void TestAssert(bool condition, std::string message);
|
||||
|
||||
|
||||
} // namespace scsl
|
||||
|
||||
#endif //SCSL_TEST_H
|
||||
48
include/sctest/Report.h
Executable file
48
include/sctest/Report.h
Executable file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// Project: scccl
|
||||
// File: include/test/Report.h
|
||||
// Author: Kyle Isom
|
||||
// Date: 2017-06-05
|
||||
// Namespace: test
|
||||
//
|
||||
// Report.h defines a Report structure that contains information about
|
||||
// the results of unit tests.
|
||||
//
|
||||
// Copyright 2017 Kyle Isom <kyle@imap.cc>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef __SCTEST_REPORT_H
|
||||
#define __SCTEST_REPORT_H
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace sctest {
|
||||
|
||||
typedef struct _Report {
|
||||
// Failing stores the number of failing tests; for tests added
|
||||
// with AddTest, this is a test that returned false. For tests
|
||||
// added with AddFailingTest, this is a test that returned true.
|
||||
size_t Failing;
|
||||
|
||||
// Total is the number of tests registered during the last run.
|
||||
size_t Total;
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> Start;
|
||||
std::chrono::time_point<std::chrono::steady_clock> End;
|
||||
std::chrono::duration<double> Duration;
|
||||
|
||||
_Report();
|
||||
} Report;
|
||||
|
||||
} // end namespace test
|
||||
#endif
|
||||
87
include/sctest/SimpleSuite.h
Executable file
87
include/sctest/SimpleSuite.h
Executable file
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// Project: scccl
|
||||
// File: include/test/SimpleSuite.h
|
||||
// Author: Kyle Isom
|
||||
// Date: 2017-06-05
|
||||
// Namespace: test
|
||||
//
|
||||
// SimpleSuite.h defines the SimpleSuite class for unit testing.
|
||||
//
|
||||
// Copyright 2017 Kyle Isom <kyle@imap.cc>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef __SCTEST_SIMPLESUITE_H
|
||||
#define __SCTEST_SIMPLESUITE_H
|
||||
|
||||
// SimpleSuite.h
|
||||
// This header file defines the interface for a simple suite of tests.
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <sctest/Report.h>
|
||||
|
||||
namespace sctest {
|
||||
|
||||
typedef struct {
|
||||
std::string name;
|
||||
std::function<bool(void)> test;
|
||||
} TestCase;
|
||||
|
||||
class SimpleSuite {
|
||||
public:
|
||||
SimpleSuite();
|
||||
|
||||
// Silence suppresses output.
|
||||
void Silence(void) { quiet = true; }
|
||||
|
||||
// Setup defines a setup function; this should be a predicate. This function
|
||||
// is called at the start of the Run method, before tests are run.
|
||||
void Setup(std::function<bool(void)> setupFn) { fnSetup = setupFn; }
|
||||
|
||||
// Teardown defines a teardown function; this should be a predicate. This
|
||||
// function is called at the end of the Run method, after all tests have run.
|
||||
void Teardown(std::function<bool(void)> teardownFn) { fnTeardown = teardownFn; }
|
||||
|
||||
// AddTest is used to add a test that is expected to return true.
|
||||
void AddTest(std::string, std::function<bool(void)>);
|
||||
|
||||
// AddFailingTest is used to add a test that is expected to return false.
|
||||
void AddFailingTest(std::string, std::function<bool(void)>);
|
||||
|
||||
bool Run(void);
|
||||
|
||||
// Reporting methods.
|
||||
|
||||
// Reset clears the report statistics.
|
||||
void Reset(void) { report.Failing = report.Total = 0; hasRun = false; };
|
||||
|
||||
// IsReportReady returns true if a report is ready.
|
||||
bool IsReportReady(void) { return hasRun; }
|
||||
|
||||
// Report returns a Report.
|
||||
Report GetReport(void);
|
||||
|
||||
private:
|
||||
bool quiet;
|
||||
std::function<bool(void)> fnSetup, fnTeardown;
|
||||
std::vector<TestCase> tests;
|
||||
|
||||
// Report functions.
|
||||
Report report;
|
||||
bool hasRun; // Have the tests been run yet?
|
||||
};
|
||||
|
||||
} // end namespace test
|
||||
#endif
|
||||
50
include/sctest/checks.h
Executable file
50
include/sctest/checks.h
Executable file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// Project: scccl
|
||||
// File: include/test/checks.h
|
||||
// Author: Kyle Isom
|
||||
// Date: 2017-06-05
|
||||
// Namespace: test.
|
||||
//
|
||||
// checks.h defines a number of macros (which are global in scope) for
|
||||
// use in test functions that return bools.
|
||||
//
|
||||
// Copyright 2017 Kyle Isom <kyle@imap.cc>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#ifndef __SCTEST_CHECKS_H
|
||||
#define __SCTEST_CHECKS_H
|
||||
|
||||
#include <scccl/math/math.h>
|
||||
|
||||
|
||||
namespace sctest {
|
||||
|
||||
|
||||
// The following checks are designed as shortcuts that just return false on certain
|
||||
// conditions.
|
||||
#define SCTEST_CHECK(x) if (!(x)) { return false; }
|
||||
#define SCTEST_CHECK_FALSE(x) if ((x)) { return false; }
|
||||
#define SCTEST_CHECK_EQ(x, y) if ((x) != (y)) { return false; }
|
||||
#define SCTEST_CHECK_NE(x, y) if ((x) == (y)) { return false; }
|
||||
#define SCTEST_CHECK_ZERO(x) if ((x) != 0) { return false; }
|
||||
#define SCTEST_CHECK_GTZ(x) if ((x) > 0) { return false; }
|
||||
#define SCTEST_CHECK_GEZ(x) if ((x) >= 0) { return false; }
|
||||
#define SCTEST_CHECK_LEZ(x) if ((x) <= 0) { return false; }
|
||||
#define SCTEST_CHECK_LTZ(x) if ((x) < 0) { return false; }
|
||||
#define SCTEST_CHECK_FEQ(x, y) { float eps; scmath::DefaultEpsilon(eps); if (!scmath::WithinTolerance((x), (y), eps)) { return false; }}
|
||||
#define SCTEST_CHECK_DEQ(x, y) { double eps; scmath::DefaultEpsilon(eps); if (!scmath::WithinTolerance((x), (y), eps)) { return false; }}
|
||||
|
||||
} // namespace test
|
||||
|
||||
|
||||
#endif
|
||||
57
include/sctest/debug.h
Executable file
57
include/sctest/debug.h
Executable file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Project: scccl
|
||||
// File: include/test/debug.h
|
||||
// Author: Kyle Isom
|
||||
// Date: 2017-06-05
|
||||
// Namespace: test
|
||||
//
|
||||
// debug.h defines assertions and other debugging functions.
|
||||
//
|
||||
// Copyright 2017 Kyle Isom <kyle@imap.cc>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#if 0
|
||||
// Disabled for now.
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#ifndef NDEBUG
|
||||
#include <cstdlib>
|
||||
#endif
|
||||
|
||||
namespace test {
|
||||
|
||||
// GenerateCoreDumps should be set at the beginning of the program, before
|
||||
// multithreading. It is *not* threadsafe.
|
||||
static bool GenerateCoreDumps = false;
|
||||
|
||||
static void
|
||||
Assert(bool cond) {
|
||||
#ifdef NDEBUG
|
||||
std::cout << "Not a debug build, skipping assertion." << std::endl;
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (!cond) {
|
||||
std::cerr << "Assertion failed in " << __func__ << "(" << __FILE__ << ":" << __LINE__ << ")" << std::endl;
|
||||
if (GenerateCoreDumps) {
|
||||
std::abort();
|
||||
}
|
||||
else {
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user