initial commit

This commit is contained in:
2026-08-07 15:56:42 +09:00
commit 91ad25aca9
1012 changed files with 159314 additions and 0 deletions
@@ -0,0 +1,143 @@
/*
-----------------------------------------------------------------------
Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
2014-2015, CWI, Amsterdam
Contact: astra@uantwerpen.be
Website: http://sf.net/projects/astra-toolbox
This file is part of the ASTRA Toolbox.
The ASTRA Toolbox is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The ASTRA Toolbox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
$Id$
*/
#ifndef _INC_ASTRA_GEOMETRYUTIL3D
#define _INC_ASTRA_GEOMETRYUTIL3D
namespace astra {
struct SConeProjection {
// the source
double fSrcX, fSrcY, fSrcZ;
// the origin ("bottom left") of the (flat-panel) detector
double fDetSX, fDetSY, fDetSZ;
// the U-edge of a detector pixel
double fDetUX, fDetUY, fDetUZ;
// the V-edge of a detector pixel
double fDetVX, fDetVY, fDetVZ;
void translate(double dx, double dy, double dz) {
fSrcX += dx;
fSrcY += dy;
fSrcZ += dz;
fDetSX += dx;
fDetSY += dy;
fDetSZ += dz;
}
void scale(double factor) {
fSrcX *= factor;
fSrcY *= factor;
fSrcZ *= factor;
fDetSX *= factor;
fDetSY *= factor;
fDetSZ *= factor;
fDetUX *= factor;
fDetUY *= factor;
fDetUZ *= factor;
fDetVX *= factor;
fDetVY *= factor;
fDetVZ *= factor;
}
};
struct SPar3DProjection {
// the ray direction
double fRayX, fRayY, fRayZ;
// the origin ("bottom left") of the (flat-panel) detector
double fDetSX, fDetSY, fDetSZ;
// the U-edge of a detector pixel
double fDetUX, fDetUY, fDetUZ;
// the V-edge of a detector pixel
double fDetVX, fDetVY, fDetVZ;
void translate(double dx, double dy, double dz) {
fDetSX += dx;
fDetSY += dy;
fDetSZ += dz;
}
void scale(double factor) {
fRayX *= factor;
fRayY *= factor;
fRayZ *= factor;
fDetSX *= factor;
fDetSY *= factor;
fDetSZ *= factor;
fDetUX *= factor;
fDetUY *= factor;
fDetUZ *= factor;
fDetVX *= factor;
fDetVY *= factor;
fDetVZ *= factor;
}
};
void computeBP_UV_Coeffs(const SPar3DProjection& proj,
double &fUX, double &fUY, double &fUZ, double &fUC,
double &fVX, double &fVY, double &fVZ, double &fVC);
void computeBP_UV_Coeffs(const SConeProjection& proj,
double &fUX, double &fUY, double &fUZ, double &fUC,
double &fVX, double &fVY, double &fVZ, double &fVC,
double &fDX, double &fDY, double &fDZ, double &fDC);
SConeProjection* genConeProjections(unsigned int iProjAngles,
unsigned int iProjU,
unsigned int iProjV,
double fOriginSourceDistance,
double fOriginDetectorDistance,
double fDetUSize,
double fDetVSize,
const float *pfAngles);
SPar3DProjection* genPar3DProjections(unsigned int iProjAngles,
unsigned int iProjU,
unsigned int iProjV,
double fDetUSize,
double fDetVSize,
const float *pfAngles);
}
#endif
+318
View File
@@ -0,0 +1,318 @@
/*
-----------------------------------------------------------------------
Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
2014-2015, CWI, Amsterdam
Contact: astra@uantwerpen.be
Website: http://sf.net/projects/astra-toolbox
This file is part of the ASTRA Toolbox.
The ASTRA Toolbox is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The ASTRA Toolbox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
$Id$
*/
#ifndef _INC_ASTRA_GLOBALS
#define _INC_ASTRA_GLOBALS
/*! \mainpage The ASTRA-toolbox
*
* <img src="../images/logo_big.png"/>
*/
//----------------------------------------------------------------------------------------
#ifdef _MSC_VER
// disable warning: 'fopen' was declared deprecated
#pragma warning (disable : 4996)
// disable warning: C++ exception handler used, but unwind semantics are not enables
#pragma warning (disable : 4530)
// disable warning: no suitable definition provided for explicit template instantiation request
#pragma warning (disable : 4661)
#endif
//----------------------------------------------------------------------------------------
// standard includes
#include <cassert>
#include <iostream>
#include <fstream>
#include <math.h>
//#include <boost/static_assert.hpp>
//#include <boost/throw_exception.hpp>
//----------------------------------------------------------------------------------------
// macro's
#define ASTRA_TOOLBOXVERSION_MAJOR 1
#define ASTRA_TOOLBOXVERSION_MINOR 7
#define ASTRA_TOOLBOXVERSION ((ASTRA_TOOLBOXVERSION_MAJOR)*100 + (ASTRA_TOOLBOXVERSION_MINOR))
#define ASTRA_TOOLBOXVERSION_STRING "1.7.1"
#define ASTRA_ASSERT(a) assert(a)
#define ASTRA_CONFIG_CHECK(value, type, msg) if (!(value)) { cout << "Configuration Error in " << type << ": " << msg << endl; return false; }
#define ASTRA_CONFIG_WARNING(type, msg) { cout << "Warning in " << type << ": " << msg << endl; }
#define ASTRA_DELETE(a) if (a) { delete a; a = NULL; }
#define ASTRA_DELETE_ARRAY(a) if (a) { delete[] a; a = NULL; }
#ifdef _MSC_VER
#ifdef DLL_EXPORTS
#define _AstraExport __declspec(dllexport)
#define EXPIMP_TEMPLATE
#else
#define _AstraExport __declspec(dllimport)
#define EXPIMP_TEMPLATE extern
#endif
#else
#define _AstraExport
#endif
//----------------------------------------------------------------------------------------
// typedefs
namespace astra {
typedef float float32;
typedef double float64;
typedef unsigned short int uint16;
typedef signed short int sint16;
typedef unsigned char uchar8;
typedef signed char schar8;
typedef int int32;
typedef short int int16;
}
//----------------------------------------------------------------------------------------
// globals vars & functions
//namespace astra {
//#define ToolboxVersion 0.1f;
//float32 getVersion() { return ToolboxVersion; }
//_AstraExport bool cudaEnabled() {
//#ifdef ASTRA_CUDA
// return true;
//#else
// return false;
//#endif
//}
//}
//----------------------------------------------------------------------------------------
// errors
namespace astra {
typedef enum {ASTRA_SUCCESS,
ASTRA_ERROR_NOT_INITIALIZED,
ASTRA_ERROR_INVALID_FILE,
ASTRA_ERROR_OUT_OF_RANGE,
ASTRA_ERROR_DIMENSION_MISMATCH,
ASTRA_ERROR_EXTERNAL_LIBRARY,
ASTRA_ERROR_ALLOCATION,
ASTRA_ERROR_NOT_IMPLEMENTED} AstraError;
}
//----------------------------------------------------------------------------------------
// variables
namespace astra {
const float32 PI = 3.14159265358979323846264338328f;
const float32 PI32 = 3.14159265358979323846264338328f;
const float32 PIdiv2 = PI / 2;
const float32 PIdiv4 = PI / 4;
const float32 eps = 1e-7f;
extern _AstraExport bool running_in_matlab;
}
//----------------------------------------------------------------------------------------
// math
namespace astra {
inline float32 cos_73s(float32 x)
{
/*
const float32 c1 = 0.999999953464f;
const float32 c2 = -0.4999999053455f;
const float32 c3 = 0.0416635846769f;
const float32 c4 = -0.0013853704264f;
const float32 c5 = 0.000023233f;
*/
const float c1= (float)0.99940307;
const float c2= (float)-0.49558072;
const float c3= (float)0.03679168;
float32 x2;
x2 = x * x;
//return (c1 + x2*(c2 + x2*(c3 + x2*(c4 + c5*x2))));
return (c1 + x2*(c2 + c3 * x2));
}
inline float32 fast_cos(float32 x)
{
int quad;
//x = fmod(x, 2*PI); // Get rid of values > 2* pi
if (x < 0) x = -x; // cos(-x) = cos(x)
quad = int(x/PIdiv2); // Get quadrant # (0 to 3)
switch (quad) {
case 0: return cos_73s(x);
case 1: return -cos_73s(PI-x);
case 2: return -cos_73s(x-PI);
case 3: return cos_73s(2*PI-x);
}
return 0.0f;
}
inline float32 fast_sin(float32 x){
return fast_cos(PIdiv2-x);
}
}
//----------------------------------------------------------------------------------------
// structs
namespace astra {
/**
* Struct for storing pixel weigths
**/
struct SPixelWeight
{
int m_iIndex;
float32 m_fWeight;
};
/**
* Struct combining some properties of a detector in 1D detector row
**/
struct SDetector2D
{
int m_iIndex;
int m_iAngleIndex;
int m_iDetectorIndex;
};
/**
* Struct combining some properties of a detector in 2D detector array
**/
struct SDetector3D
{
int m_iIndex;
int m_iAngleIndex;
int m_iDetectorIndex;
int m_iSliceIndex;
};
}
//----------------------------------------------------------------------------------------
// some toys
// safe reinterpret cast
// template <class To, class From>
// To safe_reinterpret_cast(From from)
// {
// BOOST_STATIC_ASSERT(sizeof(From) <= sizeof(To));
// return reinterpret_cast<To>(from);
// }
//----------------------------------------------------------------------------------------
// functions for testing
template<typename T>
inline void writeArray(T*** arr, int dim1, int dim2, int dim3, const std::string& filename)
{
std::ofstream out(filename.c_str());
int i1, i2, i3;
for (i1 = 0; i1 < dim1; ++i1) {
for (i2 = 0; i2 < dim2; ++i2) {
for (i3 = 0; i3 < dim3; ++i3) {
out << arr[i1][i2][i3] << " ";
}
out << std::endl;
}
out << std::endl;
}
out.close();
}
template<typename T>
inline void writeArray(T** arr, int dim1, int dim2, const std::string& filename)
{
std::ofstream out(filename.c_str());
for (int i1 = 0; i1 < dim1; i1++) {
for (int i2 = 0; i2 < dim2; i2++) {
out << arr[i1][i2] << " ";
}
out << std::endl;
}
out.close();
}
template<typename T>
inline void writeArray(T* arr, int dim1, const std::string& filename)
{
std::ofstream out(filename.c_str());
for (int i1 = 0; i1 < dim1; i1++) {
out << arr[i1] << " ";
}
out.close();
}
namespace astra {
_AstraExport inline int getVersion() { return ASTRA_TOOLBOXVERSION; }
_AstraExport inline const char* getVersionString() { return ASTRA_TOOLBOXVERSION_STRING; }
#ifdef ASTRA_CUDA
_AstraExport inline bool cudaEnabled() { return true; }
#else
_AstraExport inline bool cudaEnabled() { return false; }
#endif
}
//----------------------------------------------------------------------------------------
// portability between MSVC and Linux/gcc
#ifndef _MSC_VER
// #include "swrap.h"
#define EXPIMP_TEMPLATE
#if !defined(FORCEINLINE) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define FORCEINLINE inline __attribute__((__always_inline__))
#else
#define FORCEINLINE inline
#endif
#else
#define FORCEINLINE __forceinline
#endif
//----------------------------------------------------------------------------------------
// use pthreads on Linux and OSX
#if defined(__linux__) || defined(__MACH__)
#define USE_PTHREADS
#endif
#endif
+212
View File
@@ -0,0 +1,212 @@
/*
-----------------------------------------------------------------------
Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
2014-2015, CWI, Amsterdam
Contact: astra@uantwerpen.be
Website: http://sf.net/projects/astra-toolbox
This file is part of the ASTRA Toolbox.
The ASTRA Toolbox is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The ASTRA Toolbox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
$Id$
*/
#define CLOG_MAIN
#include "clog.h"
#include "Logging.h"
#include <cstdio>
using namespace astra;
void CLogger::enableScreen()
{
m_bEnabledScreen = true;
}
void CLogger::enableFile()
{
m_bEnabledFile = true;
}
void CLogger::enable()
{
enableScreen();
enableFile();
}
void CLogger::disableScreen()
{
m_bEnabledScreen = false;
}
void CLogger::disableFile()
{
m_bEnabledFile = false;
}
void CLogger::disable()
{
disableScreen();
disableFile();
}
void CLogger::debug(const char *sfile, int sline, const char *fmt, ...)
{
_assureIsInitialized();
va_list ap, apf;
if(m_bEnabledScreen){
va_start(ap, fmt);
clog_debug(sfile,sline,0,fmt,ap);
va_end(ap);
}
if(m_bEnabledFile && m_bFileProvided){
va_start(apf, fmt);
clog_debug(sfile,sline,1,fmt,apf);
va_end(apf);
}
}
void CLogger::info(const char *sfile, int sline, const char *fmt, ...)
{
_assureIsInitialized();
va_list ap, apf;
if(m_bEnabledScreen){
va_start(ap, fmt);
clog_info(sfile,sline,0,fmt,ap);
va_end(ap);
}
if(m_bEnabledFile && m_bFileProvided){
va_start(apf, fmt);
clog_info(sfile,sline,1,fmt,apf);
va_end(apf);
}
}
void CLogger::warn(const char *sfile, int sline, const char *fmt, ...)
{
_assureIsInitialized();
va_list ap, apf;
if(m_bEnabledScreen){
va_start(ap, fmt);
clog_warn(sfile,sline,0,fmt,ap);
va_end(ap);
}
if(m_bEnabledFile && m_bFileProvided){
va_start(apf, fmt);
clog_warn(sfile,sline,1,fmt,apf);
va_end(apf);
}
}
void CLogger::error(const char *sfile, int sline, const char *fmt, ...)
{
_assureIsInitialized();
va_list ap, apf;
if(m_bEnabledScreen){
va_start(ap, fmt);
clog_error(sfile,sline,0,fmt,ap);
va_end(ap);
}
if(m_bEnabledFile && m_bFileProvided){
va_start(apf, fmt);
clog_error(sfile,sline,1,fmt,apf);
va_end(apf);
}
}
void CLogger::_setLevel(int id, log_level m_eLevel)
{
switch(m_eLevel){
case LOG_DEBUG:
clog_set_level(id,CLOG_DEBUG);
break;
case LOG_INFO:
clog_set_level(id,CLOG_INFO);
break;
case LOG_WARN:
clog_set_level(id,CLOG_WARN);
break;
case LOG_ERROR:
clog_set_level(id,CLOG_ERROR);
break;
}
}
void CLogger::setOutputScreen(int fd, log_level m_eLevel)
{
_assureIsInitialized();
if(fd==1||fd==2){
clog_set_fd(0, fd);
}else{
error(__FILE__,__LINE__,"Invalid file descriptor");
}
_setLevel(0,m_eLevel);
}
void CLogger::setOutputFile(const char *filename, log_level m_eLevel)
{
if(m_bFileProvided){
clog_free(1);
m_bFileProvided=false;
}
if(!clog_init_path(1,filename)){
m_bFileProvided=true;
_setLevel(1,m_eLevel);
}
}
void CLogger::_assureIsInitialized()
{
if(!m_bInitialized)
{
clog_init_fd(0, 2);
clog_set_level(0, CLOG_INFO);
clog_set_fmt(0, "%l: %m\n");
m_bInitialized = true;
}
}
void CLogger::setFormatFile(const char *fmt)
{
if(m_bFileProvided){
clog_set_fmt(1,fmt);
}else{
error(__FILE__,__LINE__,"No log file specified");
}
}
void CLogger::setFormatScreen(const char *fmt)
{
clog_set_fmt(0,fmt);
}
CLogger::CLogger()
{
;
}
bool CLogger::setCallbackScreen(void (*cb)(const char *msg, size_t len)){
_assureIsInitialized();
return clog_set_cb(0,cb)==0;
}
bool CLogger::m_bEnabledScreen = true;
bool CLogger::m_bEnabledFile = true;
bool CLogger::m_bFileProvided = false;
bool CLogger::m_bInitialized = false;
+164
View File
@@ -0,0 +1,164 @@
/*
-----------------------------------------------------------------------
Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
2014-2015, CWI, Amsterdam
Contact: astra@uantwerpen.be
Website: http://sf.net/projects/astra-toolbox
This file is part of the ASTRA Toolbox.
The ASTRA Toolbox is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The ASTRA Toolbox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
$Id$
*/
#ifndef _INC_ASTRA_LOGGING
#define _INC_ASTRA_LOGGING
#include "Globals.h"
//#define ASTRA_DEBUG(...) astra::CLogger::debug(__FILE__,__LINE__, __VA_ARGS__)
//#define ASTRA_INFO(...) astra::CLogger::info(__FILE__,__LINE__, __VA_ARGS__)
//#define ASTRA_WARN(...) astra::CLogger::warn(__FILE__,__LINE__, __VA_ARGS__)
//#define ASTRA_ERROR(...) astra::CLogger::error(__FILE__,__LINE__, __VA_ARGS__)
// FIXME !!!!!!
#define ASTRA_DEBUG(...)
#define ASTRA_INFO(...)
#define ASTRA_WARN(...)
#define ASTRA_ERROR(...)
namespace astra
{
enum log_level {
LOG_DEBUG,
LOG_INFO,
LOG_WARN,
LOG_ERROR
};
class _AstraExport CLogger
{
CLogger();
~CLogger();
static bool m_bEnabledFile;
static bool m_bEnabledScreen;
static bool m_bFileProvided;
static bool m_bInitialized;
static void _assureIsInitialized();
static void _setLevel(int id, log_level m_eLevel);
public:
/**
* Writes a line to the log file (newline is added). Ignored if logging is turned off.
*
* @param sfile
* The name of the source file making this log call (e.g. __FILE__).
*
* @param sline
* The line number of the call in the source code (e.g. __LINE__).
*
* @param id
* The id of the logger to write to.
*
* @param fmt
* The format string for the message (printf formatting).
*
* @param ...
* Any additional format arguments.
*/
static void debug(const char *sfile, int sline, const char *fmt, ...);
static void info(const char *sfile, int sline, const char *fmt, ...);
static void warn(const char *sfile, int sline, const char *fmt, ...);
static void error(const char *sfile, int sline, const char *fmt, ...);
/**
* Sets the file to log to, with logging level.
*
* @param filename
* File to log to.
*
* @param m_eLevel
* Logging level (LOG_DEBUG, LOG_WARN, LOG_INFO, LOG_ERROR).
*
*/
static void setOutputFile(const char *filename, log_level m_eLevel);
/**
* Sets the screen to log to, with logging level.
*
* @param screen_fd
* Screen file descriptor (1 for stdout, 2 for stderr)
*
* @param m_eLevel
* Logging level (LOG_DEBUG, LOG_WARN, LOG_INFO, LOG_ERROR).
*
*/
static void setOutputScreen(int fd, log_level m_eLevel);
/**
* Set the format string for log messages. Here are the substitutions you may
* use:
*
* %f: Source file name generating the log call.
* %n: Source line number where the log call was made.
* %m: The message text sent to the logger (after printf formatting).
* %d: The current date, formatted using the logger's date format.
* %t: The current time, formatted using the logger's time format.
* %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR").
* %%: A literal percent sign.
*
* The default format string is "%d %t %f(%n): %l: %m\n".
*
* @param fmt
* The new format string, which must be less than 256 bytes.
* You probably will want to end this with a newline (\n).
*
*/
static void setFormatFile(const char *fmt);
static void setFormatScreen(const char *fmt);
/**
* Enable logging.
*
*/
static void enable();
static void enableScreen();
static void enableFile();
/**
* Disable logging.
*
*/
static void disable();
static void disableScreen();
static void disableFile();
/**
* Set callback function for logging to screen.
* @return whether callback was set succesfully.
*
*/
static bool setCallbackScreen(void (*cb)(const char *msg, size_t len));
};
}
#endif /* _INC_ASTRA_LOGGING */
+693
View File
@@ -0,0 +1,693 @@
/* clog: Extremely simple logger for C.
*
* Features:
* - Implemented purely as a single header file.
* - Create multiple loggers.
* - Four log levels (debug, info, warn, error).
* - Custom formats.
* - Fast.
*
* Dependencies:
* - Should conform to C89, C++98 (but requires vsnprintf, unfortunately).
* - POSIX environment.
*
* USAGE:
*
* Include this header in any file that wishes to write to logger(s). In
* exactly one file (per executable), define CLOG_MAIN first (e.g. in your
* main .c file).
*
* #define CLOG_MAIN
* #include "clog.h"
*
* This will define the actual objects that all the other units will use.
*
* Loggers are identified by integers (0 - 15). It's expected that you'll
* create meaningful constants and then refer to the loggers as such.
*
* Example:
*
* const int MY_LOGGER = 0;
*
* int main() {
* int r;
* r = clog_init_path(MY_LOGGER, "my_log.txt");
* if (r != 0) {
* fprintf(stderr, "Logger initialization failed.\n");
* return 1;
* }
* clog_info(CLOG(MY_LOGGER), "Hello, world!");
* clog_free(MY_LOGGER);
* return 0;
* }
*
* The CLOG macro used in the call to clog_info is a helper that passes the
* __FILE__ and __LINE__ parameters for you, so you don't have to type them
* every time. (It could be prettier with variadic macros, but that requires
* C99 or C++11 to be standards compliant.)
*
* Errors encountered by clog will be printed to stderr. You can suppress
* these by defining a macro called CLOG_SILENT before including clog.h.
*
* License: Do whatever you want. It would be nice if you contribute
* improvements as pull requests here:
*
* https://github.com/mmueller/clog
*
* Copyright 2013 Mike Mueller <mike@subfocal.net>.
*
* As is; no warranty is provided; use at your own risk.
*/
#ifndef __CLOG_H__
#define __CLOG_H__
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifndef _MSC_VER
#include <unistd.h>
#else
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#define open _open
#define close _close
#define write _write
#define snprintf _snprintf
#endif
/* Number of loggers that can be defined. */
#define CLOG_MAX_LOGGERS 16
/* Format strings cannot be longer than this. */
#define CLOG_FORMAT_LENGTH 256
/* Formatted times and dates should be less than this length. If they are not,
* they will not appear in the log. */
#define CLOG_DATETIME_LENGTH 256
/* Default format strings. */
#define CLOG_DEFAULT_FORMAT "%d %t %f(%n): %l: %m\n"
#define CLOG_DEFAULT_DATE_FORMAT "%Y-%m-%d"
#define CLOG_DEFAULT_TIME_FORMAT "%H:%M:%S"
#ifdef __cplusplus
extern "C" {
#endif
enum clog_level {
CLOG_DEBUG,
CLOG_INFO,
CLOG_WARN,
CLOG_ERROR
};
struct clog;
/**
* Create a new logger writing to the given file path. The file will always
* be opened in append mode.
*
* @param id
* A constant integer between 0 and 15 that uniquely identifies this logger.
*
* @param path
* Path to the file where log messages will be written.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_init_path(int id, const char *const path);
/**
* Create a new logger writing to a file descriptor.
*
* @param id
* A constant integer between 0 and 15 that uniquely identifies this logger.
*
* @param fd
* The file descriptor where log messages will be written.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_init_fd(int id, int fd);
/**
* Destroy (clean up) a logger. You should do this at the end of execution,
* or when you are done using the logger.
*
* @param id
* The id of the logger to destroy.
*/
void clog_free(int id);
#define CLOG(id) __FILE__, __LINE__, id
/**
* Log functions (one per level). Call these to write messages to the log
* file. The first three arguments can be replaced with a call to the CLOG
* macro defined above, e.g.:
*
* clog_debug(CLOG(MY_LOGGER_ID), "This is a log message.");
*
* @param sfile
* The name of the source file making this log call (e.g. __FILE__).
*
* @param sline
* The line number of the call in the source code (e.g. __LINE__).
*
* @param id
* The id of the logger to write to.
*
* @param fmt
* The format string for the message (printf formatting).
*
* @param ...
* Any additional format arguments.
*/
void clog_debug(const char *sfile, int sline, int id, const char *fmt, va_list ap);
void clog_info(const char *sfile, int sline, int id, const char *fmt, va_list ap);
void clog_warn(const char *sfile, int sline, int id, const char *fmt, va_list ap);
void clog_error(const char *sfile, int sline, int id, const char *fmt, va_list ap);
/**
* Set the minimum level of messages that should be written to the log.
* Messages below this level will not be written. By default, loggers are
* created with level == CLOG_DEBUG.
*
* @param id
* The identifier of the logger.
*
* @param level
* The new minimum log level.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_level(int id, enum clog_level level);
/**
* Set the format string used for times. See strftime(3) for how this string
* should be defined. The default format string is CLOG_DEFAULT_TIME_FORMAT.
*
* @param fmt
* The new format string, which must be less than CLOG_FORMAT_LENGTH bytes.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_time_fmt(int id, const char *fmt);
/**
* Set the format string used for dates. See strftime(3) for how this string
* should be defined. The default format string is CLOG_DEFAULT_DATE_FORMAT.
*
* @param fmt
* The new format string, which must be less than CLOG_FORMAT_LENGTH bytes.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_date_fmt(int id, const char *fmt);
/**
* Set the format string for log messages. Here are the substitutions you may
* use:
*
* %f: Source file name generating the log call.
* %n: Source line number where the log call was made.
* %m: The message text sent to the logger (after printf formatting).
* %d: The current date, formatted using the logger's date format.
* %t: The current time, formatted using the logger's time format.
* %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR").
* %%: A literal percent sign.
*
* The default format string is CLOG_DEFAULT_FORMAT.
*
* @param fmt
* The new format string, which must be less than CLOG_FORMAT_LENGTH bytes.
* You probably will want to end this with a newline (\n).
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_fmt(int id, const char *fmt);
/**
* Set the callback function.
*
* @param cb
* The new callback function.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_cb(int id, void (*cb)(const char *msg, size_t len));
/**
* Set the file descriptor.
*
* @param id
* The identifier of the logger.
*
* @param fd
* The new file descriptor.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_fd(int id, int fd);
/*
* No need to read below this point.
*/
/**
* The C logger structure.
*/
struct clog {
/* The current level of this logger. Messages below it will be dropped. */
enum clog_level level;
/* The file being written. */
int fd;
/* The format specifier. */
char fmt[CLOG_FORMAT_LENGTH];
/* Date format */
char date_fmt[CLOG_FORMAT_LENGTH];
/* Time format */
char time_fmt[CLOG_FORMAT_LENGTH];
/* Tracks whether the fd needs to be closed eventually. */
int opened;
/* Callback function for each log message. */
void (*cb)(const char *msg, size_t len);
};
void _clog_err(const char *fmt, ...);
#ifdef CLOG_MAIN
struct clog *_clog_loggers[CLOG_MAX_LOGGERS] = { 0 };
#else
extern struct clog *_clog_loggers[CLOG_MAX_LOGGERS];
#endif
#ifdef CLOG_MAIN
const char *const CLOG_LEVEL_NAMES[] = {
"Debug",
"Info",
"Warning",
"Error",
};
int
clog_init_path(int id, const char *const path)
{
int fd = open(path, O_CREAT | O_WRONLY | O_APPEND, 0666);
if (fd == -1) {
_clog_err("Unable to open %s: %s\n", path, strerror(errno));
return 1;
}
if (clog_init_fd(id, fd)) {
close(fd);
return 1;
}
_clog_loggers[id]->opened = 1;
return 0;
}
int
clog_init_fd(int id, int fd)
{
struct clog *logger;
if (_clog_loggers[id] != NULL) {
_clog_err("Logger %d already initialized.\n", id);
return 1;
}
logger = (struct clog *) malloc(sizeof(struct clog));
if (logger == NULL) {
_clog_err("Failed to allocate logger: %s\n", strerror(errno));
return 1;
}
logger->level = CLOG_DEBUG;
logger->fd = fd;
logger->opened = 0;
strcpy(logger->fmt, CLOG_DEFAULT_FORMAT);
strcpy(logger->date_fmt, CLOG_DEFAULT_DATE_FORMAT);
strcpy(logger->time_fmt, CLOG_DEFAULT_TIME_FORMAT);
logger->cb = NULL;
_clog_loggers[id] = logger;
return 0;
}
void
clog_free(int id)
{
if (_clog_loggers[id]) {
if (_clog_loggers[id]->opened) {
close(_clog_loggers[id]->fd);
}
free(_clog_loggers[id]);
_clog_loggers[id]=NULL;
}
}
int
clog_set_level(int id, enum clog_level level)
{
if (_clog_loggers[id] == NULL) {
return 1;
}
if ((unsigned) level > CLOG_ERROR) {
return 1;
}
_clog_loggers[id]->level = level;
return 0;
}
int
clog_set_fd(int id, int fd)
{
if (_clog_loggers[id] == NULL) {
return 1;
}
_clog_loggers[id]->fd = fd;
return 0;
}
int
clog_set_time_fmt(int id, const char *fmt)
{
struct clog *logger = _clog_loggers[id];
if (logger == NULL) {
_clog_err("clog_set_time_fmt: No such logger: %d\n", id);
return 1;
}
if (strlen(fmt) >= CLOG_FORMAT_LENGTH) {
_clog_err("clog_set_time_fmt: Format specifier too long.\n");
return 1;
}
strcpy(logger->time_fmt, fmt);
return 0;
}
int
clog_set_date_fmt(int id, const char *fmt)
{
struct clog *logger = _clog_loggers[id];
if (logger == NULL) {
_clog_err("clog_set_date_fmt: No such logger: %d\n", id);
return 1;
}
if (strlen(fmt) >= CLOG_FORMAT_LENGTH) {
_clog_err("clog_set_date_fmt: Format specifier too long.\n");
return 1;
}
strcpy(logger->date_fmt, fmt);
return 0;
}
int
clog_set_fmt(int id, const char *fmt)
{
struct clog *logger = _clog_loggers[id];
if (logger == NULL) {
_clog_err("clog_set_fmt: No such logger: %d\n", id);
return 1;
}
if (strlen(fmt) >= CLOG_FORMAT_LENGTH) {
_clog_err("clog_set_fmt: Format specifier too long.\n");
return 1;
}
strcpy(logger->fmt, fmt);
return 0;
}
int
clog_set_cb(int id, void (*cb)(const char *msg, size_t len))
{
struct clog *logger = _clog_loggers[id];
if (logger == NULL) {
_clog_err("clog_set_cb: No such logger: %d\n", id);
return 1;
}
logger->cb = cb;
return 0;
}
/* Internal functions */
size_t
_clog_append_str(char **dst, char *orig_buf, const char *src, size_t cur_size)
{
size_t new_size = cur_size;
while (strlen(*dst) + strlen(src) >= new_size) {
new_size *= 2;
}
if (new_size != cur_size) {
if (*dst == orig_buf) {
*dst = (char *) malloc(new_size);
strcpy(*dst, orig_buf);
} else {
*dst = (char *) realloc(*dst, new_size);
}
}
strcat(*dst, src);
return new_size;
}
size_t
_clog_append_int(char **dst, char *orig_buf, long int d, size_t cur_size)
{
char buf[40]; /* Enough for 128-bit decimal */
if (snprintf(buf, 40, "%ld", d) >= 40) {
return cur_size;
}
return _clog_append_str(dst, orig_buf, buf, cur_size);
}
size_t
_clog_append_time(char **dst, char *orig_buf, struct tm *lt,
const char *fmt, size_t cur_size)
{
char buf[CLOG_DATETIME_LENGTH];
size_t result = strftime(buf, CLOG_DATETIME_LENGTH, fmt, lt);
if (result > 0) {
return _clog_append_str(dst, orig_buf, buf, cur_size);
}
return cur_size;
}
const char *
_clog_basename(const char *path)
{
const char *slash = strrchr(path, '/');
if (slash) {
path = slash + 1;
}
#ifdef _WIN32
slash = strrchr(path, '\\');
if (slash) {
path = slash + 1;
}
#endif
return path;
}
char *
_clog_format(const struct clog *logger, char buf[], size_t buf_size,
const char *sfile, int sline, const char *level,
const char *message)
{
size_t cur_size = buf_size;
char *result = buf;
enum { NORMAL, SUBST } state = NORMAL;
size_t fmtlen = strlen(logger->fmt);
size_t i;
time_t t = time(NULL);
struct tm *lt = localtime(&t);
sfile = _clog_basename(sfile);
result[0] = 0;
for (i = 0; i < fmtlen; ++i) {
if (state == NORMAL) {
if (logger->fmt[i] == '%') {
state = SUBST;
} else {
char str[2] = { 0 };
str[0] = logger->fmt[i];
cur_size = _clog_append_str(&result, buf, str, cur_size);
}
} else {
switch (logger->fmt[i]) {
case '%':
cur_size = _clog_append_str(&result, buf, "%", cur_size);
break;
case 't':
cur_size = _clog_append_time(&result, buf, lt,
logger->time_fmt, cur_size);
break;
case 'd':
cur_size = _clog_append_time(&result, buf, lt,
logger->date_fmt, cur_size);
break;
case 'l':
cur_size = _clog_append_str(&result, buf, level, cur_size);
break;
case 'n':
cur_size = _clog_append_int(&result, buf, sline, cur_size);
break;
case 'f':
cur_size = _clog_append_str(&result, buf, sfile, cur_size);
break;
case 'm':
cur_size = _clog_append_str(&result, buf, message,
cur_size);
break;
}
state = NORMAL;
}
}
return result;
}
void
_clog_log(const char *sfile, int sline, enum clog_level level,
int id, const char *fmt, va_list ap)
{
/* For speed: Use a stack buffer until message exceeds 4096, then switch
* to dynamically allocated. This should greatly reduce the number of
* memory allocations (and subsequent fragmentation). */
char buf[4096];
size_t buf_size = 4096;
char *dynbuf = buf;
char *message;
int result;
struct clog *logger = _clog_loggers[id];
if (!logger) {
_clog_err("No such logger: %d\n", id);
return;
}
if (level < logger->level) {
return;
}
/* Format the message text with the argument list. */
result = vsnprintf(dynbuf, buf_size, fmt, ap);
if ((size_t) result >= buf_size) {
buf_size = result + 1;
dynbuf = (char *) malloc(buf_size);
result = vsnprintf(dynbuf, buf_size, fmt, ap);
if ((size_t) result >= buf_size) {
/* Formatting failed -- too large */
_clog_err("Formatting failed (1).\n");
free(dynbuf);
return;
}
}
/* Format according to log format and write to log */
{
char message_buf[4096];
message = _clog_format(logger, message_buf, 4096, sfile, sline,
CLOG_LEVEL_NAMES[level], dynbuf);
if (!message) {
_clog_err("Formatting failed (2).\n");
if (dynbuf != buf) {
free(dynbuf);
}
return;
}
result = write(logger->fd, message, strlen(message));
if (logger->cb) logger->cb(message,strlen(message));
if (result == -1) {
_clog_err("Unable to write to log file: %s\n", strerror(errno));
}
if (message != message_buf) {
free(message);
}
if (dynbuf != buf) {
free(dynbuf);
}
#ifndef _MSC_VER
fsync(logger->fd);
#else
HANDLE h = (HANDLE) _get_osfhandle(logger->fd);
if (h != INVALID_HANDLE_VALUE) {
// This call will fail on a console fd, but that's ok.
FlushFileBuffers(h);
}
#endif
}
}
void
clog_debug(const char *sfile, int sline, int id, const char *fmt, va_list ap)
{
_clog_log(sfile, sline, CLOG_DEBUG, id, fmt, ap);
}
void
clog_info(const char *sfile, int sline, int id, const char *fmt, va_list ap)
{
_clog_log(sfile, sline, CLOG_INFO, id, fmt, ap);
}
void
clog_warn(const char *sfile, int sline, int id, const char *fmt, va_list ap)
{
_clog_log(sfile, sline, CLOG_WARN, id, fmt, ap);
}
void
clog_error(const char *sfile, int sline, int id, const char *fmt, va_list ap)
{
_clog_log(sfile, sline, CLOG_ERROR, id, fmt, ap);
}
void
_clog_err(const char *fmt, ...)
{
#ifdef CLOG_SILENT
(void) fmt;
#else
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
#endif
}
#endif /* CLOG_MAIN */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* __CLOG_H__ */