htcp

Properties of water suspended in clouds
git clone git://git.meso-star.fr/htcp.git
Log | Files | Refs | README | LICENSE

commit acb5335cb8b815da1518b97f8b1cdd23157f4914
parent d5a74b886ef518818298582d821ff2aaceff8df8
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 15 May 2018 11:15:59 +0200

Rename the htcop project in htcp

Improve the README and refactor the CMakeLists files

Diffstat:
AREADME.md | 35+++++++++++++++++++++++++++++++++++
Mcmake/CMakeLists.txt | 61++++++++-----------------------------------------------------
Acmake/htcp/CMakeLists.txt | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dcmake/les2htcop/CMakeLists.txt | 78------------------------------------------------------------------------------
Acmake/les2htcp/CMakeLists.txt | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rcmake/FindNetCDF.cmake -> cmake/les2htcp/FindNetCDF.cmake | 0
Ddoc/htcop.txt | 29-----------------------------
Adoc/htcp.txt | 36++++++++++++++++++++++++++++++++++++
Dsrc/htcop.c | 335-------------------------------------------------------------------------------
Dsrc/htcop.h | 139-------------------------------------------------------------------------------
Asrc/htcp.c | 335+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/htcp.h | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/les2htcop.c | 790-------------------------------------------------------------------------------
Asrc/les2htcp.c | 790+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/les2htcop.h.in -> src/les2htcp.h.in | 0
Dsrc/test_htcop.c | 65-----------------------------------------------------------------
Dsrc/test_htcop_load.c | 120-------------------------------------------------------------------------------
Dsrc/test_htcop_load_from_file.c | 75---------------------------------------------------------------------------
Dsrc/test_htcop_utils.h | 34----------------------------------
Asrc/test_htcp.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_htcp_load.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_htcp_load_from_file.c | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_htcp_utils.h | 34++++++++++++++++++++++++++++++++++
23 files changed, 1774 insertions(+), 1718 deletions(-)

diff --git a/README.md b/README.md @@ -0,0 +1,35 @@ +# High-Tune: Cloud Properties + +This project describes the htcp binary fileformat that is used to store the +some properties of a cloud. The provided `les2htcp` command line tool extracts +these properties from a LES output stored in a +[NetCDF](https://www.unidata.ucar.edu/software/netcdf/) file, and converts them +in the `htcp` fileformat. Finally, a library is provided to load the `htcp` +fileformat. Since the data to load can be huge, this library silently +loads/unloads them dynamically allowing to process data that are too large to +fit in the main memory. + +## How to build + +The `les2htcp` program and `htcp` library are compatible GNU/Linux 64-bits. +They rely on the [CMake](http://www.cmake.org) and the +[RCMake](https://gitlab.com/vaplv/rcmake/) packages to build. They also depend +on the [RSys](https://gitlab.com/vaplv/rsys/) library. Furthermore, the +`les2htcp` tool depends on the +[NetCDF](https://www.unidata.ucar.edu/software/netcdf/) C library. + +To build them, first ensure that CMake is installed on your system. Then +install the RCMake package as well as the aforementioned prerequisites. Finally +generate the project from the `cmake/CMakeLists.txt` file by appending to the +`CMAKE_PREFIX_PATH` variable the install directories of its dependencies. The +resulting project can be edited, built, tested and installed as any CMake +project. Refer to the [CMake](https://cmake.org/documentation) for further +informations on CMake. + +## Licenses + +The `les2htcp` tool and the `htcp` library are free software copyright +(C) |Meso|Star 2018. They are released under the GPL v3+ license: GNU GPL +version 3 or later. You are welcome to redistribute them under certain +conditions; refer to the COPYING file for details. + diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -14,21 +14,14 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. cmake_minimum_required(VERSION 2.8) -project(htcop C) +project(htcp) enable_testing() option(NO_TEST "Do not build tests" OFF) -add_subdirectory(les2htcop) # Build utility - -set(HTCOP_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src) - ################################################################################ -# Check dependencies +# Prerequisites ################################################################################ -get_filename_component(_current_source_dir ${CMAKE_CURRENT_LIST_FILE} PATH) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${_current_source_dir}/) - find_package(RCMake 0.3 REQUIRED) find_package(RSys 0.6 REQUIRED) @@ -39,62 +32,24 @@ include(rcmake_runtime) include_directories(${RSys_INCLUDE_DIR}) ################################################################################ -# Configure and define targets -################################################################################ -set(VERSION_MAJOR 0) -set(VERSION_MINOR 0) -set(VERSION_PATCH 0) -set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) - -set(HTCOP_FILES_SRC htcop.c) -set(HTCOP_FILES_INC ) -set(HTCOP_FILES_INC_API htcop.h) -set(HTCOP_FILES_DOC COPYING) - -# Prepend each file in the `HTCOP_FILES_<SRC|INC>' list by `HTCOP_SOURCE_DIR' -rcmake_prepend_path(HTCOP_FILES_SRC ${HTCOP_SOURCE_DIR}) -rcmake_prepend_path(HTCOP_FILES_INC ${HTCOP_SOURCE_DIR}) -rcmake_prepend_path(HTCOP_FILES_INC_API ${HTCOP_SOURCE_DIR}) -rcmake_prepend_path(HTCOP_FILES_DOC ${PROJECT_SOURCE_DIR}/../) - -add_library(htcop SHARED ${HTCOP_FILES_SRC} ${HTCOP_FILES_INC} ${HTCOP_FILES_INC_API}) -target_link_libraries(htcop RSys) - -set_target_properties(htcop PROPERTIES - DEFINE_SYMBOL HTCOP_SHARED_BUILD - VERSION ${VERSION} - SOVERSION ${VERSION_MAJOR}) - -rcmake_setup_devel(htcop HTCOP ${VERSION} high_tune/htcop.h) - -################################################################################ -# Add tests +# Test utilities ################################################################################ if(NOT NO_TEST) function(build_test _name) add_executable(${_name} - ${HTCOP_SOURCE_DIR}/${_name}.c) - target_link_libraries(${_name} htcop) + ${HTCP_SOURCE_DIR}/${_name}.c) + target_link_libraries(${_name} htcp) endfunction() function(new_test _name) build_test(${_name}) add_test(${_name} ${_name}) endfunction() - - new_test(test_htcop) - new_test(test_htcop_load) - - build_test(test_htcop_load_from_file) endif() ################################################################################ -# Define output & install directories +# Sub projects ################################################################################ -install(TARGETS htcop - ARCHIVE DESTINATION bin - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin) -install(FILES ${HTCOP_FILES_INC_API} DESTINATION include/high_tune) -install(FILES ${HTCOP_FILES_DOC} DESTINATION share/doc/htcop) +add_subdirectory(htcp) +add_subdirectory(les2htcp) diff --git a/cmake/htcp/CMakeLists.txt b/cmake/htcp/CMakeLists.txt @@ -0,0 +1,68 @@ +# Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +cmake_minimum_required(VERSION 2.8) +project(htcp-library C) + +set(HTCP_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../../src) + +################################################################################ +# Configure and define targets +################################################################################ +set(VERSION_MAJOR 0) +set(VERSION_MINOR 0) +set(VERSION_PATCH 0) +set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) + +set(HTCP_FILES_SRC htcp.c) +set(HTCP_FILES_INC ) +set(HTCP_FILES_INC_API htcp.h) +set(HTCP_FILES_DOC COPYING) + +# Prepend each file in the `HTCP_FILES_<SRC|INC>' list by `HTCP_SOURCE_DIR' +rcmake_prepend_path(HTCP_FILES_SRC ${HTCP_SOURCE_DIR}) +rcmake_prepend_path(HTCP_FILES_INC ${HTCP_SOURCE_DIR}) +rcmake_prepend_path(HTCP_FILES_INC_API ${HTCP_SOURCE_DIR}) +rcmake_prepend_path(HTCP_FILES_DOC ${PROJECT_SOURCE_DIR}/../) + +add_library(htcp SHARED ${HTCP_FILES_SRC} ${HTCP_FILES_INC} ${HTCP_FILES_INC_API}) +target_link_libraries(htcp RSys) + +set_target_properties(htcp PROPERTIES + DEFINE_SYMBOL HTCP_SHARED_BUILD + VERSION ${VERSION} + SOVERSION ${VERSION_MAJOR}) + +rcmake_setup_devel(htcp HTCP ${VERSION} high_tune/htcp.h) + +################################################################################ +# Add tests +################################################################################ +if(NOT NO_TEST) + new_test(test_htcp) + new_test(test_htcp_load) + build_test(test_htcp_load_from_file) +endif() + +################################################################################ +# Define output & install directories +################################################################################ +install(TARGETS htcp + ARCHIVE DESTINATION bin + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) +install(FILES ${HTCP_FILES_INC_API} DESTINATION include/high_tune) +install(FILES ${HTCP_FILES_DOC} DESTINATION share/doc/htcp) + diff --git a/cmake/les2htcop/CMakeLists.txt b/cmake/les2htcop/CMakeLists.txt @@ -1,78 +0,0 @@ -# Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -cmake_minimum_required(VERSION 2.8) -project(les2htcop C) -enable_testing() - -set(LES2HTCOP_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../../src) -option(NO_TEST "Do not build tests" OFF) - -################################################################################ -# Check dependencies -################################################################################ -get_filename_component(_current_source_dir ${CMAKE_CURRENT_LIST_FILE} PATH) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${_current_source_dir}/../) - -find_package(RCMake 0.3 REQUIRED) -find_package(RSys 0.6 REQUIRED) -find_package(NetCDF REQUIRED) - -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) -include(rcmake) -include(rcmake_runtime) - -include_directories( - ${RSys_INCLUDE_DIR} - ${NETCDF_C_INCLUDE_DIRS} - ${CMAKE_CURRENT_BINARY_DIR}) - -################################################################################ -# Generate files -################################################################################ -set(VERSION_MAJOR 0) -set(VERSION_MINOR 0) -set(VERSION_PATCH 0) -set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) - -configure_file(${LES2HTCOP_SOURCE_DIR}/les2htcop.h.in - ${CMAKE_CURRENT_BINARY_DIR}/les2htcop.h @ONLY) - -################################################################################ -# Configure and define targets -################################################################################ -set(LES2HTCOP_FILES_SRC les2htcop.c) -set(LES2HTCOP_FILES_INC les2htcop.h.in) -set(LES2HTCOP_FILES_DOC COPYING) - -# Prepend each file in the `HTNC_FILES_<SRC|INC>' list by `LES2HTCOP_SOURCE_DIR' -rcmake_prepend_path(LES2HTCOP_FILES_SRC ${LES2HTCOP_SOURCE_DIR}) -rcmake_prepend_path(LES2HTCOP_FILES_INC ${LES2HTCOP_SOURCE_DIR}) -rcmake_prepend_path(LES2HTCOP_FILES_DOC ${PROJECT_SOURCE_DIR}/../../) - -add_executable(les2htcop ${LES2HTCOP_FILES_SRC}) -target_link_libraries(les2htcop RSys ${NETCDF_C_LIBRARIES}) - -set_target_properties(les2htcop PROPERTIES - DEFINE_SYMBOL LES2HTCOP_SHARED_BUILD - VERSION ${VERSION} - SOVERSION ${VERSION_MAJOR}) - -################################################################################ -# Define output & install directories -################################################################################ -install(TARGETS les2htcop RUNTIME DESTINATION bin) -install(FILES ${LES2HTCOP_FILES_DOC} DESTINATION share/doc/les2htcop) - diff --git a/cmake/les2htcp/CMakeLists.txt b/cmake/les2htcp/CMakeLists.txt @@ -0,0 +1,69 @@ +# Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +cmake_minimum_required(VERSION 2.8) +project(les2htcp C) + +set(LES2HTCP_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../../src) + +################################################################################ +# Check dependencies +################################################################################ +get_filename_component(_current_source_dir ${CMAKE_CURRENT_LIST_FILE} PATH) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${_current_source_dir}) + +find_package(NetCDF REQUIRED) + +include_directories( + ${NETCDF_C_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR}) + +################################################################################ +# Generate files +################################################################################ +set(VERSION_MAJOR 0) +set(VERSION_MINOR 0) +set(VERSION_PATCH 0) +set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) + +configure_file(${LES2HTCP_SOURCE_DIR}/les2htcp.h.in + ${CMAKE_CURRENT_BINARY_DIR}/les2htcp.h @ONLY) + +################################################################################ +# Configure and define targets +################################################################################ +set(LES2HTCP_FILES_SRC les2htcp.c) +set(LES2HTCP_FILES_INC les2htcp.h.in) +set(LES2HTCP_FILES_DOC COPYING) + +# Prepend each file in the `HTNC_FILES_<SRC|INC>' list by `LES2HTCP_SOURCE_DIR' +rcmake_prepend_path(LES2HTCP_FILES_SRC ${LES2HTCP_SOURCE_DIR}) +rcmake_prepend_path(LES2HTCP_FILES_INC ${LES2HTCP_SOURCE_DIR}) +rcmake_prepend_path(LES2HTCP_FILES_DOC ${PROJECT_SOURCE_DIR}/../../) + +add_executable(les2htcp ${LES2HTCP_FILES_SRC}) +target_link_libraries(les2htcp RSys ${NETCDF_C_LIBRARIES}) + +set_target_properties(les2htcp PROPERTIES + DEFINE_SYMBOL LES2HTCP_SHARED_BUILD + VERSION ${VERSION} + SOVERSION ${VERSION_MAJOR}) + +################################################################################ +# Define output & install directories +################################################################################ +install(TARGETS les2htcp RUNTIME DESTINATION bin) +install(FILES ${LES2HTCP_FILES_DOC} DESTINATION share/doc/les2htcp) + diff --git a/cmake/FindNetCDF.cmake b/cmake/les2htcp/FindNetCDF.cmake diff --git a/doc/htcop.txt b/doc/htcop.txt @@ -1,29 +0,0 @@ -<htnc> ::= <header> - <definition> - <lower-pos> - <voxel-size> - <padding> - <RVT> # align on pagesize - <padding> - <RCT> # align on pagesize - <padding> - -<header> ::= <pagesize> <is-Z-irregular> -<definition> ::= <#X> <#Y> <#Z> <#time> -<lower-pos> ::= <double3> -<voxel-size> ::= <double3> [DOUBLE ... ] # optionnal double for irregular Z -<RVT> ::= DOUBLE [DOUBLE ... ] -<RCT> ::= DOUBLE [DOUBLE ... ] - -<double3> ::= DOUBLE DOUBLE DOUBLE - -<#X> ::= INT32 -<#Y> ::= INT32 -<#Z> ::= INT32 -<#time> ::= INT32 - -<is-Z-irregular> ::= INT8 - -<pagesize> ::= INT64 - -<padding> ::= [ BYTES ... ] # Alignment address onto <pagesize> diff --git a/doc/htcp.txt b/doc/htcp.txt @@ -0,0 +1,36 @@ +# Copyright (C) 2018 |Meso|Star> <contact@meso-star.com> +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. This file is offered as-is, +# without any warranty. + +<htnc> ::= <header> + <definition> + <lower-pos> + <voxel-size> + <padding> + <RVT> # align on pagesize + <padding> + <RCT> # align on pagesize + <padding> + +<header> ::= <pagesize> <is-Z-irregular> +<definition> ::= <#X> <#Y> <#Z> <#time> +<lower-pos> ::= <double3> +<voxel-size> ::= <double3> [DOUBLE ... ] # optionnal double for irregular Z +<RVT> ::= DOUBLE [DOUBLE ... ] +<RCT> ::= DOUBLE [DOUBLE ... ] + +<double3> ::= DOUBLE DOUBLE DOUBLE + +<#X> ::= INT32 +<#Y> ::= INT32 +<#Z> ::= INT32 +<#time> ::= INT32 + +<is-Z-irregular> ::= INT8 + +<pagesize> ::= INT64 + +<padding> ::= [ BYTES ... ] # Alignment address onto <pagesize> diff --git a/src/htcop.c b/src/htcop.c @@ -1,335 +0,0 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#define _POSIX_C_SOURCE 200809L /* mmap support */ -#define _DEFAULT_SOURCE 1 /* MAP_POPULATE support */ - -#include "htcop.h" - -#include <rsys/dynamic_array_double.h> -#include <rsys/logger.h> -#include <rsys/ref_count.h> -#include <rsys/mem_allocator.h> - -#include <errno.h> -#include <string.h> -#include <sys/mman.h> -#include <unistd.h> - -enum { X, Y, Z, TIME }; /* Helper constants */ - -struct htcop { - int64_t pagesize; - int8_t irregular_z; - int32_t definition[4]; - double lower[3]; - double vxsz[2]; /* Size of the voxels in X and Y */ - struct darray_double vxsz_z; /* Size of the voxels along the Z dimension */ - - double* RCT; /* Mapped memory */ - double* RVT; /* Mapped memory */ - size_t RCT_length; /* In bytes */ - size_t RVT_length; /* In bytes */ - - size_t pagesize_os; /* Page size of the os */ - int verbose; /* Verbosity level */ - struct logger* logger; - struct mem_allocator* allocator; - ref_T ref; -}; - -/******************************************************************************* - * Local functions - ******************************************************************************/ -static void -log_msg - (const struct htcop* htcop, - const enum log_type stream, - const char* msg, - va_list vargs) -{ - ASSERT(htcop && msg); - if(htcop->verbose) { - res_T res; (void)res; - res = logger_vprint(htcop->logger, stream, msg, vargs); - ASSERT(res == RES_OK); - } -} - -static void -log_err(const struct htcop* htcop, const char* msg, ...) -{ - va_list vargs_list; - ASSERT(htcop && msg); - va_start(vargs_list, msg); - log_msg(htcop, LOG_ERROR, msg, vargs_list); - va_end(vargs_list); -} - -static void -reset_htcop(struct htcop* htcop) -{ - htcop->pagesize = 0; - htcop->irregular_z = 0; - htcop->definition[0] = 0; - htcop->definition[1] = 0; - htcop->definition[2] = 0; - htcop->lower[0] = -1; - htcop->lower[1] = -1; - htcop->lower[2] = -1; - htcop->vxsz[0] = -1; - htcop->vxsz[1] = -1; - darray_double_clear(&htcop->vxsz_z); - if(htcop->RCT) munmap(htcop->RCT, htcop->RCT_length); - if(htcop->RVT) munmap(htcop->RVT, htcop->RVT_length); - htcop->RCT = NULL; - htcop->RVT = NULL; - htcop->RCT_length = 0; - htcop->RVT_length = 0; -} - -static res_T -load_stream(struct htcop* htcop, FILE* stream, const char* stream_name) -{ - size_t nz = 0; - size_t map_len = 0; - off_t offset = 0; - res_T res = RES_OK; - ASSERT(htcop && stream && stream_name); - - reset_htcop(htcop); - - #define READ(Var, N, Name) { \ - if(fread((Var), sizeof(*(Var)), (N), stream) != (N)) { \ - fprintf(stderr, "%s: could not read the %s\n", stream_name, Name); \ - res = RES_IO_ERR; \ - goto error; \ - } \ - } (void)0 - READ(&htcop->pagesize, 1, "page size"); - if(!IS_POW2(htcop->pagesize)) { - log_err(htcop, "%s: invalid page size `%li'. It must be a power of two.\n", - stream_name, (long)htcop->pagesize); - res = RES_BAD_ARG; - goto error; - } - - if(!IS_ALIGNED(htcop->pagesize, htcop->pagesize_os)) { - log_err(htcop, -"%s: invalid page size `%li'. The page size attribute must be aligned on the " -"page size of the operating system , i.e. %lu.\n", - stream_name, (long)htcop->pagesize, (unsigned long)htcop->pagesize_os); - res = RES_BAD_ARG; - goto error; - } - - READ(&htcop->irregular_z, 1, "'irregular Z' flag"); - READ(htcop->definition, 4, "spatial and time definitions"); - if(htcop->definition[0] <= 0 - || htcop->definition[1] <= 0 - || htcop->definition[2] <= 0 - || htcop->definition[3] <= 0) { - log_err(htcop, -"%s: the spatial/time definition cannot be negative or null -- spatial " -"definition: %i %i %i; time definition: %i\n", - stream_name, SPLIT3(htcop->definition), htcop->definition[TIME]); - res = RES_BAD_ARG; - goto error; - } - - READ(htcop->lower, 3, "lower position"); - READ(htcop->vxsz, 2, "XY voxel size "); - - nz = htcop->irregular_z ? (size_t)htcop->definition[Z] : 1; - res = darray_double_resize(&htcop->vxsz_z, nz); - if(res != RES_OK) { - log_err(htcop, - "%s: could not allocate memory to store the size of the voxels in Z.\n", - stream_name); - res = RES_MEM_ERR; - goto error; - } - READ(darray_double_data_get(&htcop->vxsz_z), nz, "Z voxel size(s)"); - #undef READ - - map_len = - (size_t)htcop->definition[X] - * (size_t)htcop->definition[Y] - * (size_t)htcop->definition[Z] - * (size_t)htcop->definition[TIME] - * sizeof(double); - /* Align sizeof mapped data onto page size */ - map_len = ALIGN_SIZE(map_len, (size_t)htcop->pagesize); - - /* Ensure that offset is align on page size */ - offset = ALIGN_SIZE(ftell(stream), htcop->pagesize); - - #define MMAP(Var) { \ - htcop->Var = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE|MAP_POPULATE, \ - fileno(stream), offset); \ - if(htcop->Var == MAP_FAILED) { \ - log_err(htcop, "%s: could not map the "STR(Var)" data -- %s.\n", \ - stream_name, strerror(errno)); \ - res = RES_IO_ERR; \ - goto error; \ - } \ - htcop->Var##_length = map_len; \ - } (void)0 - MMAP(RVT); offset += (off_t)map_len; - MMAP(RCT); offset += (off_t)map_len; - #undef MMAP - - CHK(fseek(stream, offset, SEEK_CUR) != -1); - -exit: - return res; -error: - goto exit; -} - -static void -release_htcop(ref_T* ref) -{ - struct htcop* htcop; - ASSERT(ref); - htcop = CONTAINER_OF(ref, struct htcop, ref); - reset_htcop(htcop); - darray_double_release(&htcop->vxsz_z); - MEM_RM(htcop->allocator, htcop); -} - -/******************************************************************************* - * Exported functions - ******************************************************************************/ -res_T -htcop_create - (struct logger* log, - struct mem_allocator* mem_allocator, - const int verbose, - struct htcop** out_htcop) -{ - struct htcop* htcop = NULL; - struct mem_allocator* allocator = NULL; - struct logger* logger = NULL; - res_T res = RES_OK; - - if(!out_htcop) { - res = RES_BAD_ARG; - goto error; - } - - allocator = mem_allocator ? mem_allocator : &mem_default_allocator; - logger = log ? log : LOGGER_DEFAULT; - - htcop = MEM_CALLOC(allocator, 1, sizeof(struct htcop)); - if(!htcop) { - if(verbose) { - logger_print(logger, LOG_ERROR, - "%s: could not allocate the HTCOP handler.\n", FUNC_NAME); - res = RES_MEM_ERR; - goto error; - } - } - ref_init(&htcop->ref); - htcop->allocator = allocator; - htcop->logger = logger; - htcop->verbose = verbose; - htcop->pagesize_os = (size_t)sysconf(_SC_PAGESIZE); - darray_double_init(htcop->allocator, &htcop->vxsz_z); - -exit: - if(out_htcop) *out_htcop = htcop; - return res; -error: - if(htcop) { - HTCOP(ref_put(htcop)); - htcop = NULL; - } - goto exit; -} - -res_T -htcop_ref_get(struct htcop* htcop) -{ - if(!htcop) return RES_BAD_ARG; - ref_get(&htcop->ref); - return RES_OK; -} - -res_T -htcop_ref_put(struct htcop* htcop) -{ - if(!htcop) return RES_BAD_ARG; - ref_put(&htcop->ref, release_htcop); - return RES_OK; -} - -res_T -htcop_load(struct htcop* htcop, const char* path) -{ - FILE* file = NULL; - res_T res = RES_OK; - - if(!htcop || !path) { - res = RES_BAD_ARG; - goto error; - } - - file = fopen(path, "r"); - if(!file) { - log_err(htcop, "%s: error opening file `%S'.\n", FUNC_NAME, path); - res = RES_IO_ERR; - goto error; - } - - res = load_stream(htcop, file, path); - if(res != RES_OK) goto error; - -exit: - if(file) fclose(file); - return res; -error: - goto exit; -} - -res_T -htcop_load_stream(struct htcop* htcop, FILE* stream) -{ - if(!htcop || !stream) return RES_BAD_ARG; - return load_stream(htcop, stream, "<stream>"); -} - -res_T -htcop_get_desc(const struct htcop* htcop, struct htcop_desc* desc) -{ - if(!htcop || !desc) return RES_BAD_ARG; - if(!htcop->RVT) return RES_BAD_ARG; - desc->pagesize = (size_t)htcop->pagesize; - desc->irregular_z = htcop->irregular_z != 0; - desc->spatial_definition[0] = (size_t)htcop->definition[X]; - desc->spatial_definition[1] = (size_t)htcop->definition[Y]; - desc->spatial_definition[2] = (size_t)htcop->definition[Z]; - desc->time_definition = (size_t)htcop->definition[TIME]; - desc->lower[0] = htcop->lower[0]; - desc->lower[1] = htcop->lower[1]; - desc->lower[2] = htcop->lower[2]; - desc->vxsz_x = htcop->vxsz[0]; - desc->vxsz_y = htcop->vxsz[1]; - desc->vxsz_z = darray_double_cdata_get(&htcop->vxsz_z); - desc->RCT = htcop->RCT; - desc->RVT = htcop->RVT; - return RES_OK; -} - diff --git a/src/htcop.h b/src/htcop.h @@ -1,139 +0,0 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#ifndef HTCOP_H -#define HTCOP_H - -#include <rsys/rsys.h> - -/* Library symbol management */ -#if defined(HTCOP_SHARED_BUILD) /* Build shared library */ - #define HTCOP_API extern EXPORT_SYM -#elif defined(HTCOP_STATIC) /* Use/build static library */ - #define HTCOP_API extern LOCAL_SYM -#else /* Use shared library */ - #define HTCOP_API extern IMPORT_SYM -#endif - -/* Helper macro that asserts if the invocation of the htcop function `Func' - * returns an error. One should use this macro on htcop function calls for - * which no explicit error checking is performed */ -#ifndef NDEBUG - #define HTCOP(Func) ASSERT(htcop_ ## Func == RES_OK) -#else - #define HTCOP(Func) htcop_ ## Func -#endif - -/* Forward declaration of external data types */ -struct logger; -struct mem_allocator; - -/* Forward declaration of opaque data types */ -struct htcop; - -struct htcop_desc { - size_t pagesize; /* In bytes */ - int irregular_z; - - /* #coordinates along the X, Y and Z axis. X means for the West-Est axis, Y - * is the South-North axis and finally Z is the altitude */ - size_t spatial_definition[3]; - size_t time_definition; /* Definition of the time */ - - double lower[3]; /* Lower position of the grid */ - double vxsz_x; /* Voxel size in X */ - double vxsz_y; /* Voxel size in Y */ - const double* vxsz_z; /* Voxel size along Z */ - - const double* RCT; - const double* RVT; -}; -#define HTCOP_DESC_NULL__ {0,-1,{0,0,0},0,{-1,-1,-1},-1,-1,NULL,NULL,NULL} -static const struct htcop_desc HTCOP_DESC_NULL = HTCOP_DESC_NULL__; - -BEGIN_DECLS - -/******************************************************************************* - * HTCOP API - ******************************************************************************/ -HTCOP_API res_T -htcop_create -(struct logger* logger, /* NULL <=> use default logger */ - struct mem_allocator* allocator, /* NULL <=> use default allocator */ - const int verbose, /* Verbosity level */ - struct htcop** htcop); - -HTCOP_API res_T -htcop_load - (struct htcop* htcop, - const char* path); - -HTCOP_API res_T -htcop_load_stream - (struct htcop* htcop, - FILE* stream); - -HTCOP_API res_T -htcop_ref_get - (struct htcop* htcop); - -HTCOP_API res_T -htcop_ref_put - (struct htcop* htcop); - -HTCOP_API res_T -htcop_get_desc - (const struct htcop* htcop, - struct htcop_desc* desc); - -/* Internal function */ -static FINLINE double -htcop_dblgrid4D_at__ - (const double* grid, - const struct htcop_desc* desc, - size_t x, size_t y, size_t z, size_t t) -{ - size_t row, slice, array; - ASSERT(desc && grid); - ASSERT(x < desc->spatial_definition[0]); - ASSERT(y < desc->spatial_definition[1]); - ASSERT(z < desc->spatial_definition[2]); - ASSERT(t < desc->time_definition); - row = desc->spatial_definition[0]; - slice = desc->spatial_definition[1] * row; - array = desc->spatial_definition[2] * slice; - return grid[t*array + z*slice + y*row + x]; -} - -static FINLINE double -htcop_desc_RCT_at - (const struct htcop_desc* desc, - size_t x, size_t y, size_t z, size_t t) -{ - return htcop_dblgrid4D_at__(desc->RCT, desc, x, y, z, t); -} - - -static FINLINE double -htcop_desc_RVT_at - (const struct htcop_desc* desc, - size_t x, size_t y, size_t z, size_t t) -{ - return htcop_dblgrid4D_at__(desc->RVT, desc, x, y, z, t); -} - -END_DECLS - -#endif /* HTCOP_H */ diff --git a/src/htcp.c b/src/htcp.c @@ -0,0 +1,335 @@ +/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200809L /* mmap support */ +#define _DEFAULT_SOURCE 1 /* MAP_POPULATE support */ + +#include "htcp.h" + +#include <rsys/dynamic_array_double.h> +#include <rsys/logger.h> +#include <rsys/ref_count.h> +#include <rsys/mem_allocator.h> + +#include <errno.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> + +enum { X, Y, Z, TIME }; /* Helper constants */ + +struct htcp { + int64_t pagesize; + int8_t irregular_z; + int32_t definition[4]; + double lower[3]; + double vxsz[2]; /* Size of the voxels in X and Y */ + struct darray_double vxsz_z; /* Size of the voxels along the Z dimension */ + + double* RCT; /* Mapped memory */ + double* RVT; /* Mapped memory */ + size_t RCT_length; /* In bytes */ + size_t RVT_length; /* In bytes */ + + size_t pagesize_os; /* Page size of the os */ + int verbose; /* Verbosity level */ + struct logger* logger; + struct mem_allocator* allocator; + ref_T ref; +}; + +/******************************************************************************* + * Local functions + ******************************************************************************/ +static void +log_msg + (const struct htcp* htcp, + const enum log_type stream, + const char* msg, + va_list vargs) +{ + ASSERT(htcp && msg); + if(htcp->verbose) { + res_T res; (void)res; + res = logger_vprint(htcp->logger, stream, msg, vargs); + ASSERT(res == RES_OK); + } +} + +static void +log_err(const struct htcp* htcp, const char* msg, ...) +{ + va_list vargs_list; + ASSERT(htcp && msg); + va_start(vargs_list, msg); + log_msg(htcp, LOG_ERROR, msg, vargs_list); + va_end(vargs_list); +} + +static void +reset_htcp(struct htcp* htcp) +{ + htcp->pagesize = 0; + htcp->irregular_z = 0; + htcp->definition[0] = 0; + htcp->definition[1] = 0; + htcp->definition[2] = 0; + htcp->lower[0] = -1; + htcp->lower[1] = -1; + htcp->lower[2] = -1; + htcp->vxsz[0] = -1; + htcp->vxsz[1] = -1; + darray_double_clear(&htcp->vxsz_z); + if(htcp->RCT) munmap(htcp->RCT, htcp->RCT_length); + if(htcp->RVT) munmap(htcp->RVT, htcp->RVT_length); + htcp->RCT = NULL; + htcp->RVT = NULL; + htcp->RCT_length = 0; + htcp->RVT_length = 0; +} + +static res_T +load_stream(struct htcp* htcp, FILE* stream, const char* stream_name) +{ + size_t nz = 0; + size_t map_len = 0; + off_t offset = 0; + res_T res = RES_OK; + ASSERT(htcp && stream && stream_name); + + reset_htcp(htcp); + + #define READ(Var, N, Name) { \ + if(fread((Var), sizeof(*(Var)), (N), stream) != (N)) { \ + fprintf(stderr, "%s: could not read the %s\n", stream_name, Name); \ + res = RES_IO_ERR; \ + goto error; \ + } \ + } (void)0 + READ(&htcp->pagesize, 1, "page size"); + if(!IS_POW2(htcp->pagesize)) { + log_err(htcp, "%s: invalid page size `%li'. It must be a power of two.\n", + stream_name, (long)htcp->pagesize); + res = RES_BAD_ARG; + goto error; + } + + if(!IS_ALIGNED(htcp->pagesize, htcp->pagesize_os)) { + log_err(htcp, +"%s: invalid page size `%li'. The page size attribute must be aligned on the " +"page size of the operating system , i.e. %lu.\n", + stream_name, (long)htcp->pagesize, (unsigned long)htcp->pagesize_os); + res = RES_BAD_ARG; + goto error; + } + + READ(&htcp->irregular_z, 1, "'irregular Z' flag"); + READ(htcp->definition, 4, "spatial and time definitions"); + if(htcp->definition[0] <= 0 + || htcp->definition[1] <= 0 + || htcp->definition[2] <= 0 + || htcp->definition[3] <= 0) { + log_err(htcp, +"%s: the spatial/time definition cannot be negative or null -- spatial " +"definition: %i %i %i; time definition: %i\n", + stream_name, SPLIT3(htcp->definition), htcp->definition[TIME]); + res = RES_BAD_ARG; + goto error; + } + + READ(htcp->lower, 3, "lower position"); + READ(htcp->vxsz, 2, "XY voxel size "); + + nz = htcp->irregular_z ? (size_t)htcp->definition[Z] : 1; + res = darray_double_resize(&htcp->vxsz_z, nz); + if(res != RES_OK) { + log_err(htcp, + "%s: could not allocate memory to store the size of the voxels in Z.\n", + stream_name); + res = RES_MEM_ERR; + goto error; + } + READ(darray_double_data_get(&htcp->vxsz_z), nz, "Z voxel size(s)"); + #undef READ + + map_len = + (size_t)htcp->definition[X] + * (size_t)htcp->definition[Y] + * (size_t)htcp->definition[Z] + * (size_t)htcp->definition[TIME] + * sizeof(double); + /* Align sizeof mapped data onto page size */ + map_len = ALIGN_SIZE(map_len, (size_t)htcp->pagesize); + + /* Ensure that offset is align on page size */ + offset = ALIGN_SIZE(ftell(stream), htcp->pagesize); + + #define MMAP(Var) { \ + htcp->Var = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE|MAP_POPULATE, \ + fileno(stream), offset); \ + if(htcp->Var == MAP_FAILED) { \ + log_err(htcp, "%s: could not map the "STR(Var)" data -- %s.\n", \ + stream_name, strerror(errno)); \ + res = RES_IO_ERR; \ + goto error; \ + } \ + htcp->Var##_length = map_len; \ + } (void)0 + MMAP(RVT); offset += (off_t)map_len; + MMAP(RCT); offset += (off_t)map_len; + #undef MMAP + + CHK(fseek(stream, offset, SEEK_CUR) != -1); + +exit: + return res; +error: + goto exit; +} + +static void +release_htcp(ref_T* ref) +{ + struct htcp* htcp; + ASSERT(ref); + htcp = CONTAINER_OF(ref, struct htcp, ref); + reset_htcp(htcp); + darray_double_release(&htcp->vxsz_z); + MEM_RM(htcp->allocator, htcp); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +htcp_create + (struct logger* log, + struct mem_allocator* mem_allocator, + const int verbose, + struct htcp** out_htcp) +{ + struct htcp* htcp = NULL; + struct mem_allocator* allocator = NULL; + struct logger* logger = NULL; + res_T res = RES_OK; + + if(!out_htcp) { + res = RES_BAD_ARG; + goto error; + } + + allocator = mem_allocator ? mem_allocator : &mem_default_allocator; + logger = log ? log : LOGGER_DEFAULT; + + htcp = MEM_CALLOC(allocator, 1, sizeof(struct htcp)); + if(!htcp) { + if(verbose) { + logger_print(logger, LOG_ERROR, + "%s: could not allocate the HTCP handler.\n", FUNC_NAME); + res = RES_MEM_ERR; + goto error; + } + } + ref_init(&htcp->ref); + htcp->allocator = allocator; + htcp->logger = logger; + htcp->verbose = verbose; + htcp->pagesize_os = (size_t)sysconf(_SC_PAGESIZE); + darray_double_init(htcp->allocator, &htcp->vxsz_z); + +exit: + if(out_htcp) *out_htcp = htcp; + return res; +error: + if(htcp) { + HTCP(ref_put(htcp)); + htcp = NULL; + } + goto exit; +} + +res_T +htcp_ref_get(struct htcp* htcp) +{ + if(!htcp) return RES_BAD_ARG; + ref_get(&htcp->ref); + return RES_OK; +} + +res_T +htcp_ref_put(struct htcp* htcp) +{ + if(!htcp) return RES_BAD_ARG; + ref_put(&htcp->ref, release_htcp); + return RES_OK; +} + +res_T +htcp_load(struct htcp* htcp, const char* path) +{ + FILE* file = NULL; + res_T res = RES_OK; + + if(!htcp || !path) { + res = RES_BAD_ARG; + goto error; + } + + file = fopen(path, "r"); + if(!file) { + log_err(htcp, "%s: error opening file `%S'.\n", FUNC_NAME, path); + res = RES_IO_ERR; + goto error; + } + + res = load_stream(htcp, file, path); + if(res != RES_OK) goto error; + +exit: + if(file) fclose(file); + return res; +error: + goto exit; +} + +res_T +htcp_load_stream(struct htcp* htcp, FILE* stream) +{ + if(!htcp || !stream) return RES_BAD_ARG; + return load_stream(htcp, stream, "<stream>"); +} + +res_T +htcp_get_desc(const struct htcp* htcp, struct htcp_desc* desc) +{ + if(!htcp || !desc) return RES_BAD_ARG; + if(!htcp->RVT) return RES_BAD_ARG; + desc->pagesize = (size_t)htcp->pagesize; + desc->irregular_z = htcp->irregular_z != 0; + desc->spatial_definition[0] = (size_t)htcp->definition[X]; + desc->spatial_definition[1] = (size_t)htcp->definition[Y]; + desc->spatial_definition[2] = (size_t)htcp->definition[Z]; + desc->time_definition = (size_t)htcp->definition[TIME]; + desc->lower[0] = htcp->lower[0]; + desc->lower[1] = htcp->lower[1]; + desc->lower[2] = htcp->lower[2]; + desc->vxsz_x = htcp->vxsz[0]; + desc->vxsz_y = htcp->vxsz[1]; + desc->vxsz_z = darray_double_cdata_get(&htcp->vxsz_z); + desc->RCT = htcp->RCT; + desc->RVT = htcp->RVT; + return RES_OK; +} + diff --git a/src/htcp.h b/src/htcp.h @@ -0,0 +1,139 @@ +/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef HTCP_H +#define HTCP_H + +#include <rsys/rsys.h> + +/* Library symbol management */ +#if defined(HTCP_SHARED_BUILD) /* Build shared library */ + #define HTCP_API extern EXPORT_SYM +#elif defined(HTCP_STATIC) /* Use/build static library */ + #define HTCP_API extern LOCAL_SYM +#else /* Use shared library */ + #define HTCP_API extern IMPORT_SYM +#endif + +/* Helper macro that asserts if the invocation of the htcp function `Func' + * returns an error. One should use this macro on htcp function calls for + * which no explicit error checking is performed */ +#ifndef NDEBUG + #define HTCP(Func) ASSERT(htcp_ ## Func == RES_OK) +#else + #define HTCP(Func) htcp_ ## Func +#endif + +/* Forward declaration of external data types */ +struct logger; +struct mem_allocator; + +/* Forward declaration of opaque data types */ +struct htcp; + +struct htcp_desc { + size_t pagesize; /* In bytes */ + int irregular_z; + + /* #coordinates along the X, Y and Z axis. X means for the West-Est axis, Y + * is the South-North axis and finally Z is the altitude */ + size_t spatial_definition[3]; + size_t time_definition; /* Definition of the time */ + + double lower[3]; /* Lower position of the grid */ + double vxsz_x; /* Voxel size in X */ + double vxsz_y; /* Voxel size in Y */ + const double* vxsz_z; /* Voxel size along Z */ + + const double* RCT; + const double* RVT; +}; +#define HTCP_DESC_NULL__ {0,-1,{0,0,0},0,{-1,-1,-1},-1,-1,NULL,NULL,NULL} +static const struct htcp_desc HTCP_DESC_NULL = HTCP_DESC_NULL__; + +BEGIN_DECLS + +/******************************************************************************* + * HTCP API + ******************************************************************************/ +HTCP_API res_T +htcp_create +(struct logger* logger, /* NULL <=> use default logger */ + struct mem_allocator* allocator, /* NULL <=> use default allocator */ + const int verbose, /* Verbosity level */ + struct htcp** htcp); + +HTCP_API res_T +htcp_load + (struct htcp* htcp, + const char* path); + +HTCP_API res_T +htcp_load_stream + (struct htcp* htcp, + FILE* stream); + +HTCP_API res_T +htcp_ref_get + (struct htcp* htcp); + +HTCP_API res_T +htcp_ref_put + (struct htcp* htcp); + +HTCP_API res_T +htcp_get_desc + (const struct htcp* htcp, + struct htcp_desc* desc); + +/* Internal function */ +static FINLINE double +htcp_dblgrid4D_at__ + (const double* grid, + const struct htcp_desc* desc, + size_t x, size_t y, size_t z, size_t t) +{ + size_t row, slice, array; + ASSERT(desc && grid); + ASSERT(x < desc->spatial_definition[0]); + ASSERT(y < desc->spatial_definition[1]); + ASSERT(z < desc->spatial_definition[2]); + ASSERT(t < desc->time_definition); + row = desc->spatial_definition[0]; + slice = desc->spatial_definition[1] * row; + array = desc->spatial_definition[2] * slice; + return grid[t*array + z*slice + y*row + x]; +} + +static FINLINE double +htcp_desc_RCT_at + (const struct htcp_desc* desc, + size_t x, size_t y, size_t z, size_t t) +{ + return htcp_dblgrid4D_at__(desc->RCT, desc, x, y, z, t); +} + + +static FINLINE double +htcp_desc_RVT_at + (const struct htcp_desc* desc, + size_t x, size_t y, size_t z, size_t t) +{ + return htcp_dblgrid4D_at__(desc->RVT, desc, x, y, z, t); +} + +END_DECLS + +#endif /* HTCP_H */ diff --git a/src/les2htcop.c b/src/les2htcop.c @@ -1,790 +0,0 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#define _POSIX_C_SOURCE 200112L /* getopt/close support */ - -#include "les2htcop.h" - -#include <rsys/cstr.h> -#include <rsys/math.h> -#include <rsys/mem_allocator.h> -#include <rsys/rsys.h> - -#include <alloca.h> -#include <netcdf.h> -#include <string.h> -#include <unistd.h> /* getopt */ -#include <fcntl.h> /* open */ -#include <sys/stat.h> /* S_IRUSR & S_IWUSR */ - -/******************************************************************************* - * Command arguments - ******************************************************************************/ -struct args { - const char* output; - const char* input; - long pagesize; - int force_overwrite; - int check; - int no_output; - int quit; /* Quit the application */ -}; -#define ARGS_DEFAULT__ {NULL,NULL,4096,0,0,0,0} -static const struct args ARGS_DEFAULT = ARGS_DEFAULT__; - -static void -print_help(const char* cmd) -{ - ASSERT(cmd); - - printf( -"Usage: %s -i INPUT [OPTIONS]\n" -"Convert the LES data stored into INPUT from NetCDF to the htcop fileformat.\n\n", - cmd); - printf( -" -c advanced check of the validity of the submitted LES file\n" -" with respect to the converter pre-requisites. Note that\n" -" this option can increase significantly the conversion\n" -" time.\n"); - printf( -" -f overwrite the OUTPUT file if it already exists.\n"); - printf( -" -h display this help and exit.\n"); - printf( -" -i INPUT path of the LES file to convert.\n"); - printf( -" -o OUTPUT write results to OUTPUT. If not defined, write results to\n" -" standard output.\n"); - printf( -" -p targeted page size in bytes. Must be a power of 2. The size\n" -" of the converted LES data and their address into output are\n" -" aligned according to this size. By default, the page size\n" -" is %li Bytes.\n", - ARGS_DEFAULT.pagesize); - printf( -" -q do not write results to OUTPUT.\n"); - printf( -" -v display version information and exit.\n"); - printf("\n"); - printf( -"%s (C) 2018 |Meso|Star>. This is free software released under the GNU GPL\n" -"license, version 3 or later. You are free to change or redistribute it under\n" -"certain conditions <http://gnu.org/licenses/gpl.html>.\n", cmd); -} - -static void -args_release(struct args* args) -{ - ASSERT(args); - *args = ARGS_DEFAULT; -} - -static res_T -args_init(struct args* args, const int argc, char** argv) -{ - int opt; - res_T res = RES_OK; - ASSERT(args && argc && argv); - - while((opt = getopt(argc, argv, "cfhi:o:p:qv")) != -1) { - switch(opt) { - case 'c': args->check = 1; break; - case 'f': args->force_overwrite = 1; break; - case 'h': - print_help(argv[0]); - args_release(args); - args->quit = 1; - goto exit; - case 'i': args->input = optarg; break; - case 'o': args->output = optarg; break; - case 'p': - res = cstr_to_long(optarg, &args->pagesize); - if(res == RES_OK && !IS_POW2(args->pagesize)) res = RES_BAD_ARG; - break; - case 'q': args->no_output = 1; break; - case 'v': - printf("%s %d.%d.%d\n", - argv[0], - LES2HTLES_VERSION_MAJOR, - LES2HTLES_VERSION_MINOR, - LES2HTLES_VERSION_PATCH); - args->quit = 1; - goto exit; - default: RES_BAD_ARG; break; - } - if(res != RES_OK) { - if(optarg) { - fprintf(stderr, "%s: invalid option argumet '%s' -- '%c'\n", - argv[0], optarg, opt); - } - goto error; - } - } - - if(!args->input) { - fprintf(stderr, "Missing input file.\n"); - res = RES_BAD_ARG; - goto error; - } - - if(args->no_output) args->output = NULL; - -exit: - return res; -error: - args_release(args); - goto exit; -} - -/******************************************************************************* - * Grid header - ******************************************************************************/ -struct grid { - int32_t nx, ny, nz, ntimes; /* Definition */ - int8_t is_z_irregular; - double lower[3]; /* Lower position of the grid */ - double vxsz_x; /* Size of the voxel is X */ - double vxsz_y; /* Size of the voxel in Y */ - double* vxsz_z; /* Size of the voxel in Z */ -}; - -#define GRID_NULL__ {0,0,0,0,0,{0,0,0},0,0,NULL} -static const struct grid GRID_NULL = GRID_NULL__; - -static void -grid_release(struct grid* grid) -{ - CHK(grid); - if(grid->vxsz_z) mem_rm(grid->vxsz_z); -} - -/******************************************************************************* - * NetCDF helper functions and macros - ******************************************************************************/ -#define INVALID_ID -1 - -#define NC(Func) { \ - const int err__ = nc_ ## Func; \ - if(err__ != NC_NOERR) { \ - fprintf(stderr, "error:%i:%s\n", __LINE__, ncerr_to_str(err__)); \ - abort(); \ - } \ - } (void)0 - -static INLINE const char* -ncerr_to_str(const int ncerr) -{ - const char* str = "NC_ERR_<UNKNOWN>"; - switch(ncerr) { - case NC_EBADGRPID: str = "NC_EBADGRPID"; break; - case NC_EBADID: str = "NC_EBADID"; break; - case NC_EBADNAME: str = "NC_EBADNAME"; break; - case NC_ECHAR: str = "NC_ECHAR"; break; - case NC_EDIMMETA: str = "NC_EDIMMETA"; break; - case NC_EHDFERR: str = "NC_EHDFERR"; break; - case NC_ENOMEM: str = "NC_ENOMEM"; break; - case NC_ENOTATT: str = "NC_ENOTATT"; break; - case NC_ENOTVAR: str = "NC_ENOTVAR"; break; - case NC_ERANGE: str = "NC_ERANGE"; break; - case NC_NOERR: str = "NC_NOERR"; break; - } - return str; -} - -static INLINE const char* -nctype_to_str(const nc_type type) -{ - const char* str = "NC_TYPE_<UNKNOWN>"; - switch(type) { - case NC_NAT: str = "NC_NAT"; break; - case NC_BYTE: str = "NC_BYTE"; break; - case NC_CHAR: str = "NC_CHAR"; break; - case NC_SHORT: str = "NC_SHORT"; break; - case NC_LONG: str = "NC_LONG"; break; - case NC_FLOAT: str = "NC_FLOAT"; break; - case NC_DOUBLE: str = "NC_DOUBLE"; break; - case NC_UBYTE: str = "NC_UBYTE"; break; - case NC_USHORT: str = "NC_USHORT"; break; - case NC_UINT: str = "NC_UINT"; break; - case NC_INT64: str = "NC_INT64"; break; - case NC_UINT64: str = "NC_UINT64"; break; - case NC_STRING: str = "NC_STRING"; break; - default: FATAL("Unreachable code.\n"); break; - } - return str; -} - -static INLINE size_t -sizeof_nctype(const nc_type type) -{ - size_t sz; - switch(type) { - case NC_BYTE: - case NC_CHAR: - case NC_UBYTE: - sz = 1; break; - case NC_SHORT: - case NC_USHORT: - sz = 2; break; - case NC_FLOAT: - case NC_INT: - case NC_UINT: - sz = 4; break; - case NC_DOUBLE: - case NC_INT64: - case NC_UINT64: - sz = 8; break; - default: FATAL("Unreachable cde.\n"); break; - } - return sz; -} - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static res_T -open_output_stream(const char* path, const int force_overwrite, FILE** stream) -{ - int fd = -1; - FILE* fp = NULL; - res_T res = RES_OK; - ASSERT(path); - - if(force_overwrite) { - fp = fopen(path, "w"); - if(!fp) { - fprintf(stderr, "Could not open the output file '%s'.\n", path); - goto error; - } - } else { - fd = open(path, O_CREAT|O_WRONLY|O_EXCL|O_TRUNC, S_IRUSR|S_IWUSR); - if(fd >= 0) { - fp = fdopen(fd, "w"); - if(fp == NULL) { - fprintf(stderr, "Could not open the output file '%s'.\n", path); - goto error; - } - } else if(errno == EEXIST) { - fprintf(stderr, - "The output file '%s' already exists. Use -f to overwrite it.\n", path); - goto error; - } else { - fprintf(stderr, - "Unexpected error while opening the output file `'%s'.\n", path); - goto error; - } - } - -exit: - *stream = fp; - return res; -error: - res = RES_IO_ERR; - if(fp) { - CHK(fclose(fp) == 0); - fp = NULL; - } else if(fd >= 0) { - CHK(close(fd) == 0); - } - goto exit; -} - - -static res_T -setup_definition - (int nc, int32_t* nx, int32_t* ny, int32_t* nz, int32_t* ntimes) -{ - size_t len[4]; - int dimids[4]; - int id; - int ndims; - int err = NC_NOERR; - res_T res = RES_OK; - ASSERT(nx && ny && nz && ntimes); - - err = nc_inq_varid(nc, "RCT", &id); - if(err != NC_NOERR) { - fprintf(stderr, "Could not inquire the 'RCT' variable -- %s\n", - ncerr_to_str(err)); - res = RES_BAD_ARG; - goto error; - } - - NC(inq_varndims(nc, id, &ndims)); - if(ndims != 4) { - fprintf(stderr, "The dimension of the 'RCT' variable must be 4.\n"); - res = RES_BAD_ARG; - goto error; - } - - NC(inq_vardimid(nc, id, dimids)); - NC(inq_dimlen(nc, dimids[0], len+0)); - NC(inq_dimlen(nc, dimids[1], len+1)); - NC(inq_dimlen(nc, dimids[2], len+2)); - NC(inq_dimlen(nc, dimids[3], len+3)); - - *nx = (int32_t)len[3]; - *ny = (int32_t)len[2]; - *nz = (int32_t)len[1]; - *ntimes = (int32_t)len[0]; - - if(*nx < 1 || *ny < 1 || *nz < 1 || *ntimes < 1) { - fprintf(stderr, - "The spatial and time definitions cannot be null.\n" - " #x = %i; #y = %i; #z = %i; #time = %i\n", - *nx, *ny, *nz, *ntimes); - res = RES_BAD_ARG; - goto error; - } - -exit: - return res; -error: - goto exit; -} - -static res_T -setup_regular_dimension - (const int nc, - const char* var_name, - double* voxel_size, - double* lower_pos, - const int check) -{ - char* mem = NULL; - size_t len, start, i; - double vxsz; - nc_type type; - int varid, dimid, ndims; - int err = NC_NOERR; - res_T res = RES_OK; - ASSERT(nc && var_name && voxel_size && lower_pos); - - err = nc_inq_varid(nc, var_name, &varid); - if(err != NC_NOERR) { - fprintf(stderr, "Could not inquire the '%s' variable -- %s\n", - var_name, ncerr_to_str(err)); - res = RES_BAD_ARG; - goto error; - } - NC(inq_varndims(nc, varid, &ndims)); - if(ndims != 1) { - fprintf(stderr, "The dimension of the '%s' variable must be 1 .\n", var_name); - res = RES_BAD_ARG; - goto error; - } - - NC(inq_vardimid(nc, varid, &dimid)); - NC(inq_dimlen(nc, dimid, &len)); - ASSERT(len > 0); - - NC(inq_vartype(nc, varid, &type)); - if(type != NC_DOUBLE && type != NC_FLOAT) { - fprintf(stderr, "The type of the '%s' variable must be float or double.\n", - var_name); - res = RES_BAD_ARG; - goto error; - } - - #define AT(Mem, Id) (type == NC_FLOAT ? ((float*)Mem)[Id] : ((double*)Mem)[Id]) - if(!check) { - char* raw = alloca(8); - start = 0; - len = 1; - NC(get_vara(nc, varid, &start, &len, raw)); - vxsz = AT(raw, 0) * 2; /* Assume that the grid starts at 0 */ - } else { - mem = mem_alloc(len*sizeof_nctype(type)); - if(!mem) { - fprintf(stderr, "Could not allocate memory for the '%s' variable.\n", var_name); - res = RES_MEM_ERR; - goto error; - } - start = 0; - NC(get_vara(nc, varid, &start, &len, mem)); - vxsz = AT(mem, 0) * 2; /* Assume that the grid starts from 0 */ - } - - if(vxsz <= 0) { - fprintf(stderr, "The '%s' variable can't have negative or null values.\n", - var_name); - res = RES_BAD_ARG; - goto error; - } - - if(check) { - /* Check that the dimension is regular */ - FOR_EACH(i, 1, len) { - if(!eq_eps(AT(mem, i) - AT(mem, i-1), vxsz, 1.e-6)) { - fprintf(stderr, "The voxel size of the '%s' variable must be regular.\n", - var_name); - res = RES_BAD_ARG; - goto error; - } - } - } - #undef AT - - *voxel_size = vxsz; - *lower_pos = 0; -exit: - if(mem) mem_rm(mem); - return res; -error: - goto exit; -} - -/* Return 1 if Z is irregular */ -static int -setup_Z_dimension - (const int nc, - double** voxel_size, - double* lower_pos, - int8_t* is_z_irregular, - const int check) -{ - enum { Z, X, Y, NDIMS }; /* Helper constants */ - char* mem = NULL; /* Memory where NetCDF data are copied */ - char* mem2 = NULL; /* Temporary memory used to check the VLEV data */ - size_t len[NDIMS]; - size_t start[NDIMS]; - size_t dim_len[NDIMS]; - size_t z; /* Id over the Z dimension */ - size_t i; /* Common id */ - double vxsz; /* Voxel size */ - double* pvxsz = NULL; /* Pointer toward voxel sizes */ - nc_type type; /* Type of the NetCDF VLEV variable */ - int varid; /* NetCDF id of the VLEV variable */ - int dimids[NDIMS]; /* NetCDF id of the VLEV dimensions */ - int irregular; /* Boolean defining if the Z dimension is irregular */ - int ndims; /* #dims of the VLEV variable */ - int err = NC_NOERR; - res_T res = RES_OK; - ASSERT(nc && voxel_size && lower_pos && is_z_irregular); - - /* Retrieve the VLEV variable */ - err = nc_inq_varid(nc, "VLEV", &varid); - if(err != NC_NOERR) { - fprintf(stderr, "Could not inquire the 'VLEV' variable -- %s\n", - ncerr_to_str(err)); - res = RES_BAD_ARG; - goto error; - } - - /* Inquire the VLEV dimensionality */ - NC(inq_varndims(nc, varid, &ndims)); - if(ndims != NDIMS) { - fprintf(stderr, "The dimension of the 'VLEV' variable must be %i .\n", - NDIMS); - res = RES_BAD_ARG; - goto error; - } - NC(inq_vardimid(nc, varid, dimids)); - FOR_EACH(i, 0, NDIMS) NC(inq_dimlen(nc, dimids[i], len+i)); - - /* Inquire the type of the VLEV variable */ - NC(inq_vartype(nc, varid, &type)); - if(type != NC_DOUBLE && type != NC_FLOAT) { - fprintf(stderr, "The type of 'VLEV' variable must be float or double.\n"); - res = RES_BAD_ARG; - goto error; - } - - /* Allocate te memory space where a Z column of the VLEV variable is going to - * be copied */ - mem = mem_alloc(len[Z]*sizeof_nctype(type)); - if(!mem) { - fprintf(stderr, "Could not allocate memory for the 'VLEV' variable.\n"); - res = RES_MEM_ERR; - goto error; - } - - /* Get only one Z column */ - start[X] = 0; dim_len[X] = 1; - start[Y] = 0; dim_len[Y] = 1; - start[Z] = 0; dim_len[Z] = len[Z]; - NC(get_vara(nc, varid, start, dim_len, mem)); - - #define AT(Mem, Id) (type == NC_FLOAT ? ((float*)Mem)[Id] : ((double*)Mem)[Id]) - - /* Assume that the Z dimension starts from 0 */ - vxsz = AT(mem, 0) * 2.0; - if(vxsz <= 0) { - fprintf(stderr, "The 'VLEV' variable can't have negative or null values.\n"); - res = RES_BAD_ARG; - goto error; - } - - /* Check if the Z dimension is regular */ - FOR_EACH(z, 1, len[Z]) { - if(!eq_eps(AT(mem, z) - AT(mem, z-1), vxsz, 1.e-6)) break; - } - irregular = (z != len[Z]); - - /* Setup the size of the voxel */ - pvxsz = mem_alloc(sizeof(double) * (irregular ? len[Z] : 1)); - if(!pvxsz) { - fprintf(stderr, - "Could not allocate memory for the voxel size along the Z dimension.\n"); - res = RES_MEM_ERR; - goto error; - } - pvxsz[0] = vxsz; - - if(irregular) { - FOR_EACH(z, 1, len[Z]) { - pvxsz[z] = (AT(mem, z) - (AT(mem, z-1) + pvxsz[z-1]*0.5)) * 2.0; - if(pvxsz[z] <= 0) { - fprintf(stderr, - "The 'VLEV' variable can't have negative or null values.\n"); - res = RES_BAD_ARG; - goto error; - } - } - } - - /* Check that the others columns have the same voxel size */ - if(check) { - size_t ix, iy; - - mem2 = mem_alloc(len[Z]*sizeof_nctype(type)); - if(!mem2) { - fprintf(stderr, - "Could not allocate memory to check the 'VLEV' variable.\n"); - res = RES_MEM_ERR; - goto error; - } - - FOR_EACH(iy, 0, len[Y]) { - start[Y] = iy; - FOR_EACH(ix, 1, len[X]) { - start[X] = ix; - NC(get_vara(nc, varid, start, dim_len, mem2)); - FOR_EACH(z, 0, len[Z]) { - if(!eq_eps(AT(mem2, z), AT(mem, z), 1.e-6)) { - fprintf(stderr, - "The Z columns of the 'VLEV' variable must be the same.\n"); - res = RES_BAD_ARG; - goto error; - } - } - } - } - } - #undef AT - - *lower_pos = 0; - *voxel_size = pvxsz; - *is_z_irregular = (int8_t)irregular; - -exit: - if(mem2) mem_rm(mem2); - if(mem) mem_rm(mem); - return res; -error: - goto exit; -} - -static res_T -write_data(int nc, const char* var_name, FILE* stream) -{ - enum { TIME, Z, X, Y, NDIMS }; /* Helper constants */ - - char* mem = NULL; /* Memory where NetCDF data are copied */ - char* mem2 = NULL; /* Tmp memory where NetCDF data are converted */ - size_t start[NDIMS]; - size_t dim_len[NDIMS]; - size_t len[NDIMS]; - size_t slice_len; /* #elements per slice the 3D grid */ - size_t i; /* Common id */ - size_t t; /* Id over the time dimension */ - size_t z; /* Id over the Z dimension */ - nc_type type; - int varid; /* NetCDF id of the data to write */ - int ndims; /* #dimensions of the NetCDF data to write */ - int dimids[NDIMS]; /* NetCDF id of the data dimension */ - int err = NC_NOERR; - res_T res = RES_OK; - CHK(var_name); - - /* Retrieve the NetCDF id of the variable */ - err = nc_inq_varid(nc, var_name, &varid); - if(err != NC_NOERR) { - fprintf(stderr, "Could not inquire the '%s' variable -- %s\n", - var_name, ncerr_to_str(err)); - res = RES_BAD_ARG; - goto error; - } - - /* Inquire the dimensions of the variable */ - NC(inq_varndims(nc, varid, &ndims)); - if(ndims != NDIMS) { - fprintf(stderr, "The dimension of the '%s' variable must be %i .\n", - var_name, NDIMS); - res = RES_BAD_ARG; - goto error; - } - NC(inq_vardimid(nc, varid, dimids)); - FOR_EACH(i, 0, NDIMS) NC(inq_dimlen(nc, dimids[i], len+i)); - - /* Inquire the type of the variable */ - NC(inq_vartype(nc, varid, &type)); - if(type != NC_DOUBLE && type != NC_FLOAT) { - fprintf(stderr, - "The type of the '%s' variable cannot be %s. Expecting floating point data.\n", - var_name, nctype_to_str(type)); - res = RES_BAD_ARG; - goto error; - } - - /* Alloc the memory space where the variable data wille be copied. Note that, - * in order to reduce memory consumption, only one slice is read at a given - * time and Z position. */ - slice_len = len[X]*len[Y]; /* # elements per slice */ - mem = mem_alloc(slice_len*sizeof_nctype(type)); - - /* Allocate a temporary memory space to store the variable data converted in - * double precision if they are stored in single precision. */ - mem2 = type==NC_DOUBLE ? mem : mem_alloc(slice_len*sizeof_nctype(NC_DOUBLE)); - - if(!mem || !mem2) { - fprintf(stderr, "Could not allocate memory for the '%s' variable.\n", - var_name); - res = RES_MEM_ERR; - goto error; - } - - /* Read the whole slice */ - start[X] = 0; dim_len[X] = len[X]; - start[Y] = 0; dim_len[Y] = len[Y]; - - FOR_EACH(t, 0, len[TIME]) { - start[TIME] = t, dim_len[TIME] = 1; - FOR_EACH(z, 0, len[Z]) { - size_t n; - start[Z] = z; dim_len[Z] = 1; - - NC(get_vara(nc, varid, start, dim_len, mem)); /* Fetch the slice data */ - - /* In order to be compliant with the htgop fileformat, single precision - * data must be converted in double precision */ - if(type == NC_FLOAT) { - FOR_EACH(i, 0, slice_len) ((double*)mem2)[i] = ((float*)mem)[i]; - } - - /* Write data */ - n = fwrite(mem2, sizeof_nctype(NC_DOUBLE), slice_len, stream); - if(n != slice_len) { - fprintf(stderr, "Error writing data of the '%s' variable -- '%s'.\n", - var_name, strerror(errno)); - res = RES_IO_ERR; - goto error; - } - } - } - -exit: - if(mem2 != mem) mem_rm(mem2); - if(mem) mem_rm(mem); - return res; -error: - goto exit; -} - -/******************************************************************************* - * Program - ******************************************************************************/ -int -main(int argc, char** argv) -{ - FILE* stream = stdout; - struct grid grid = GRID_NULL; - struct args args = ARGS_DEFAULT; - int err = 0; - int err_nc = NC_NOERR; - int nc = INVALID_ID; - res_T res = RES_OK; - - res = args_init(&args, argc, argv); - if(res != RES_OK) goto error; - if(args.quit) goto exit; - - if(args.output) { - res = open_output_stream(args.output, args.force_overwrite, &stream); - if(res != RES_OK) goto error; - } - - err_nc = nc_open(args.input, NC_WRITE|NC_MMAP, &nc); - if(err_nc != NC_NOERR) { - fprintf(stderr, "Error opening file `%s' -- %s.\n", - args.input, ncerr_to_str(err_nc)); - goto exit; - } - - #define CALL(Func) { if(RES_OK != (res = Func)) goto error; } (void)0 - CALL(setup_definition(nc, &grid.nx, &grid.ny, &grid.nz, &grid.ntimes)); - CALL(setup_regular_dimension - (nc, "W_E_direction", &grid.vxsz_x, grid.lower+0, args.check)); - CALL(setup_regular_dimension - (nc, "S_N_direction", &grid.vxsz_y, grid.lower+1, args.check)); - CALL(setup_Z_dimension - (nc, &grid.vxsz_z, grid.lower+2, &grid.is_z_irregular, args.check)); - #undef CALL - - #define WRITE(Var, N, Name) { \ - if(fwrite((Var), sizeof(*(Var)), (N), stream) != (N)) { \ - fprintf(stderr, "Error writing the %s.\n", (Name)); \ - res = RES_IO_ERR; \ - goto error; \ - } \ - } (void)0 - if(!args.no_output) { - const int64_t pagesize = (int64_t)args.pagesize; - - WRITE(&pagesize, 1, "page size"); - WRITE(&grid.is_z_irregular, 1, "'irregular' Z boolean"); - WRITE(&grid.nx, 1, "X definition"); - WRITE(&grid.ny, 1, "Y definition"); - WRITE(&grid.nz, 1, "Z definition"); - WRITE(&grid.ntimes, 1, "time definition"); - WRITE(grid.lower, 3, "lower position"); - WRITE(&grid.vxsz_x, 1, "X voxel size"); - WRITE(&grid.vxsz_y, 1, "Y voxel size"); - WRITE(grid.vxsz_z, grid.is_z_irregular?(size_t)grid.nz:1, "Z voxel size(s)"); - /* padding */ - fseek(stream, ALIGN_SIZE(ftell(stream), (off_t)pagesize), SEEK_SET); - write_data(nc, "RVT", stream); - /* padding */ - fseek(stream, ALIGN_SIZE(ftell(stream), (off_t)pagesize), SEEK_SET); - write_data(nc, "RCT", stream); - /*padding */ - fseek(stream, ALIGN_SIZE(ftell(stream), (off_t)pagesize), SEEK_SET); - } - #undef WRITE - -exit: - grid_release(&grid); - if(nc != INVALID_ID) NC(close(nc)); - if(mem_allocated_size() != 0) { - fprintf(stderr, "Memory leaks: %lu Bytes\n", - (unsigned long)mem_allocated_size()); - err = -1; - } - return err; -error: - err = -1; - goto exit; -} - diff --git a/src/les2htcp.c b/src/les2htcp.c @@ -0,0 +1,790 @@ +/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200112L /* getopt/close support */ + +#include "les2htcp.h" + +#include <rsys/cstr.h> +#include <rsys/math.h> +#include <rsys/mem_allocator.h> +#include <rsys/rsys.h> + +#include <alloca.h> +#include <netcdf.h> +#include <string.h> +#include <unistd.h> /* getopt */ +#include <fcntl.h> /* open */ +#include <sys/stat.h> /* S_IRUSR & S_IWUSR */ + +/******************************************************************************* + * Command arguments + ******************************************************************************/ +struct args { + const char* output; + const char* input; + long pagesize; + int force_overwrite; + int check; + int no_output; + int quit; /* Quit the application */ +}; +#define ARGS_DEFAULT__ {NULL,NULL,4096,0,0,0,0} +static const struct args ARGS_DEFAULT = ARGS_DEFAULT__; + +static void +print_help(const char* cmd) +{ + ASSERT(cmd); + + printf( +"Usage: %s -i INPUT [OPTIONS]\n" +"Convert the LES data stored into INPUT from NetCDF to the htcp fileformat.\n\n", + cmd); + printf( +" -c advanced check of the validity of the submitted LES file\n" +" with respect to the converter pre-requisites. Note that\n" +" this option can increase significantly the conversion\n" +" time.\n"); + printf( +" -f overwrite the OUTPUT file if it already exists.\n"); + printf( +" -h display this help and exit.\n"); + printf( +" -i INPUT path of the LES file to convert.\n"); + printf( +" -o OUTPUT write results to OUTPUT. If not defined, write results to\n" +" standard output.\n"); + printf( +" -p targeted page size in bytes. Must be a power of 2. The size\n" +" of the converted LES data and their address into output are\n" +" aligned according to this size. By default, the page size\n" +" is %li Bytes.\n", + ARGS_DEFAULT.pagesize); + printf( +" -q do not write results to OUTPUT.\n"); + printf( +" -v display version information and exit.\n"); + printf("\n"); + printf( +"%s (C) 2018 |Meso|Star>. This is free software released under the GNU GPL\n" +"license, version 3 or later. You are free to change or redistribute it under\n" +"certain conditions <http://gnu.org/licenses/gpl.html>.\n", cmd); +} + +static void +args_release(struct args* args) +{ + ASSERT(args); + *args = ARGS_DEFAULT; +} + +static res_T +args_init(struct args* args, const int argc, char** argv) +{ + int opt; + res_T res = RES_OK; + ASSERT(args && argc && argv); + + while((opt = getopt(argc, argv, "cfhi:o:p:qv")) != -1) { + switch(opt) { + case 'c': args->check = 1; break; + case 'f': args->force_overwrite = 1; break; + case 'h': + print_help(argv[0]); + args_release(args); + args->quit = 1; + goto exit; + case 'i': args->input = optarg; break; + case 'o': args->output = optarg; break; + case 'p': + res = cstr_to_long(optarg, &args->pagesize); + if(res == RES_OK && !IS_POW2(args->pagesize)) res = RES_BAD_ARG; + break; + case 'q': args->no_output = 1; break; + case 'v': + printf("%s %d.%d.%d\n", + argv[0], + LES2HTLES_VERSION_MAJOR, + LES2HTLES_VERSION_MINOR, + LES2HTLES_VERSION_PATCH); + args->quit = 1; + goto exit; + default: RES_BAD_ARG; break; + } + if(res != RES_OK) { + if(optarg) { + fprintf(stderr, "%s: invalid option argumet '%s' -- '%c'\n", + argv[0], optarg, opt); + } + goto error; + } + } + + if(!args->input) { + fprintf(stderr, "Missing input file.\n"); + res = RES_BAD_ARG; + goto error; + } + + if(args->no_output) args->output = NULL; + +exit: + return res; +error: + args_release(args); + goto exit; +} + +/******************************************************************************* + * Grid header + ******************************************************************************/ +struct grid { + int32_t nx, ny, nz, ntimes; /* Definition */ + int8_t is_z_irregular; + double lower[3]; /* Lower position of the grid */ + double vxsz_x; /* Size of the voxel is X */ + double vxsz_y; /* Size of the voxel in Y */ + double* vxsz_z; /* Size of the voxel in Z */ +}; + +#define GRID_NULL__ {0,0,0,0,0,{0,0,0},0,0,NULL} +static const struct grid GRID_NULL = GRID_NULL__; + +static void +grid_release(struct grid* grid) +{ + CHK(grid); + if(grid->vxsz_z) mem_rm(grid->vxsz_z); +} + +/******************************************************************************* + * NetCDF helper functions and macros + ******************************************************************************/ +#define INVALID_ID -1 + +#define NC(Func) { \ + const int err__ = nc_ ## Func; \ + if(err__ != NC_NOERR) { \ + fprintf(stderr, "error:%i:%s\n", __LINE__, ncerr_to_str(err__)); \ + abort(); \ + } \ + } (void)0 + +static INLINE const char* +ncerr_to_str(const int ncerr) +{ + const char* str = "NC_ERR_<UNKNOWN>"; + switch(ncerr) { + case NC_EBADGRPID: str = "NC_EBADGRPID"; break; + case NC_EBADID: str = "NC_EBADID"; break; + case NC_EBADNAME: str = "NC_EBADNAME"; break; + case NC_ECHAR: str = "NC_ECHAR"; break; + case NC_EDIMMETA: str = "NC_EDIMMETA"; break; + case NC_EHDFERR: str = "NC_EHDFERR"; break; + case NC_ENOMEM: str = "NC_ENOMEM"; break; + case NC_ENOTATT: str = "NC_ENOTATT"; break; + case NC_ENOTVAR: str = "NC_ENOTVAR"; break; + case NC_ERANGE: str = "NC_ERANGE"; break; + case NC_NOERR: str = "NC_NOERR"; break; + } + return str; +} + +static INLINE const char* +nctype_to_str(const nc_type type) +{ + const char* str = "NC_TYPE_<UNKNOWN>"; + switch(type) { + case NC_NAT: str = "NC_NAT"; break; + case NC_BYTE: str = "NC_BYTE"; break; + case NC_CHAR: str = "NC_CHAR"; break; + case NC_SHORT: str = "NC_SHORT"; break; + case NC_LONG: str = "NC_LONG"; break; + case NC_FLOAT: str = "NC_FLOAT"; break; + case NC_DOUBLE: str = "NC_DOUBLE"; break; + case NC_UBYTE: str = "NC_UBYTE"; break; + case NC_USHORT: str = "NC_USHORT"; break; + case NC_UINT: str = "NC_UINT"; break; + case NC_INT64: str = "NC_INT64"; break; + case NC_UINT64: str = "NC_UINT64"; break; + case NC_STRING: str = "NC_STRING"; break; + default: FATAL("Unreachable code.\n"); break; + } + return str; +} + +static INLINE size_t +sizeof_nctype(const nc_type type) +{ + size_t sz; + switch(type) { + case NC_BYTE: + case NC_CHAR: + case NC_UBYTE: + sz = 1; break; + case NC_SHORT: + case NC_USHORT: + sz = 2; break; + case NC_FLOAT: + case NC_INT: + case NC_UINT: + sz = 4; break; + case NC_DOUBLE: + case NC_INT64: + case NC_UINT64: + sz = 8; break; + default: FATAL("Unreachable cde.\n"); break; + } + return sz; +} + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static res_T +open_output_stream(const char* path, const int force_overwrite, FILE** stream) +{ + int fd = -1; + FILE* fp = NULL; + res_T res = RES_OK; + ASSERT(path); + + if(force_overwrite) { + fp = fopen(path, "w"); + if(!fp) { + fprintf(stderr, "Could not open the output file '%s'.\n", path); + goto error; + } + } else { + fd = open(path, O_CREAT|O_WRONLY|O_EXCL|O_TRUNC, S_IRUSR|S_IWUSR); + if(fd >= 0) { + fp = fdopen(fd, "w"); + if(fp == NULL) { + fprintf(stderr, "Could not open the output file '%s'.\n", path); + goto error; + } + } else if(errno == EEXIST) { + fprintf(stderr, + "The output file '%s' already exists. Use -f to overwrite it.\n", path); + goto error; + } else { + fprintf(stderr, + "Unexpected error while opening the output file `'%s'.\n", path); + goto error; + } + } + +exit: + *stream = fp; + return res; +error: + res = RES_IO_ERR; + if(fp) { + CHK(fclose(fp) == 0); + fp = NULL; + } else if(fd >= 0) { + CHK(close(fd) == 0); + } + goto exit; +} + + +static res_T +setup_definition + (int nc, int32_t* nx, int32_t* ny, int32_t* nz, int32_t* ntimes) +{ + size_t len[4]; + int dimids[4]; + int id; + int ndims; + int err = NC_NOERR; + res_T res = RES_OK; + ASSERT(nx && ny && nz && ntimes); + + err = nc_inq_varid(nc, "RCT", &id); + if(err != NC_NOERR) { + fprintf(stderr, "Could not inquire the 'RCT' variable -- %s\n", + ncerr_to_str(err)); + res = RES_BAD_ARG; + goto error; + } + + NC(inq_varndims(nc, id, &ndims)); + if(ndims != 4) { + fprintf(stderr, "The dimension of the 'RCT' variable must be 4.\n"); + res = RES_BAD_ARG; + goto error; + } + + NC(inq_vardimid(nc, id, dimids)); + NC(inq_dimlen(nc, dimids[0], len+0)); + NC(inq_dimlen(nc, dimids[1], len+1)); + NC(inq_dimlen(nc, dimids[2], len+2)); + NC(inq_dimlen(nc, dimids[3], len+3)); + + *nx = (int32_t)len[3]; + *ny = (int32_t)len[2]; + *nz = (int32_t)len[1]; + *ntimes = (int32_t)len[0]; + + if(*nx < 1 || *ny < 1 || *nz < 1 || *ntimes < 1) { + fprintf(stderr, + "The spatial and time definitions cannot be null.\n" + " #x = %i; #y = %i; #z = %i; #time = %i\n", + *nx, *ny, *nz, *ntimes); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + goto exit; +} + +static res_T +setup_regular_dimension + (const int nc, + const char* var_name, + double* voxel_size, + double* lower_pos, + const int check) +{ + char* mem = NULL; + size_t len, start, i; + double vxsz; + nc_type type; + int varid, dimid, ndims; + int err = NC_NOERR; + res_T res = RES_OK; + ASSERT(nc && var_name && voxel_size && lower_pos); + + err = nc_inq_varid(nc, var_name, &varid); + if(err != NC_NOERR) { + fprintf(stderr, "Could not inquire the '%s' variable -- %s\n", + var_name, ncerr_to_str(err)); + res = RES_BAD_ARG; + goto error; + } + NC(inq_varndims(nc, varid, &ndims)); + if(ndims != 1) { + fprintf(stderr, "The dimension of the '%s' variable must be 1 .\n", var_name); + res = RES_BAD_ARG; + goto error; + } + + NC(inq_vardimid(nc, varid, &dimid)); + NC(inq_dimlen(nc, dimid, &len)); + ASSERT(len > 0); + + NC(inq_vartype(nc, varid, &type)); + if(type != NC_DOUBLE && type != NC_FLOAT) { + fprintf(stderr, "The type of the '%s' variable must be float or double.\n", + var_name); + res = RES_BAD_ARG; + goto error; + } + + #define AT(Mem, Id) (type == NC_FLOAT ? ((float*)Mem)[Id] : ((double*)Mem)[Id]) + if(!check) { + char* raw = alloca(8); + start = 0; + len = 1; + NC(get_vara(nc, varid, &start, &len, raw)); + vxsz = AT(raw, 0) * 2; /* Assume that the grid starts at 0 */ + } else { + mem = mem_alloc(len*sizeof_nctype(type)); + if(!mem) { + fprintf(stderr, "Could not allocate memory for the '%s' variable.\n", var_name); + res = RES_MEM_ERR; + goto error; + } + start = 0; + NC(get_vara(nc, varid, &start, &len, mem)); + vxsz = AT(mem, 0) * 2; /* Assume that the grid starts from 0 */ + } + + if(vxsz <= 0) { + fprintf(stderr, "The '%s' variable can't have negative or null values.\n", + var_name); + res = RES_BAD_ARG; + goto error; + } + + if(check) { + /* Check that the dimension is regular */ + FOR_EACH(i, 1, len) { + if(!eq_eps(AT(mem, i) - AT(mem, i-1), vxsz, 1.e-6)) { + fprintf(stderr, "The voxel size of the '%s' variable must be regular.\n", + var_name); + res = RES_BAD_ARG; + goto error; + } + } + } + #undef AT + + *voxel_size = vxsz; + *lower_pos = 0; +exit: + if(mem) mem_rm(mem); + return res; +error: + goto exit; +} + +/* Return 1 if Z is irregular */ +static int +setup_Z_dimension + (const int nc, + double** voxel_size, + double* lower_pos, + int8_t* is_z_irregular, + const int check) +{ + enum { Z, X, Y, NDIMS }; /* Helper constants */ + char* mem = NULL; /* Memory where NetCDF data are copied */ + char* mem2 = NULL; /* Temporary memory used to check the VLEV data */ + size_t len[NDIMS]; + size_t start[NDIMS]; + size_t dim_len[NDIMS]; + size_t z; /* Id over the Z dimension */ + size_t i; /* Common id */ + double vxsz; /* Voxel size */ + double* pvxsz = NULL; /* Pointer toward voxel sizes */ + nc_type type; /* Type of the NetCDF VLEV variable */ + int varid; /* NetCDF id of the VLEV variable */ + int dimids[NDIMS]; /* NetCDF id of the VLEV dimensions */ + int irregular; /* Boolean defining if the Z dimension is irregular */ + int ndims; /* #dims of the VLEV variable */ + int err = NC_NOERR; + res_T res = RES_OK; + ASSERT(nc && voxel_size && lower_pos && is_z_irregular); + + /* Retrieve the VLEV variable */ + err = nc_inq_varid(nc, "VLEV", &varid); + if(err != NC_NOERR) { + fprintf(stderr, "Could not inquire the 'VLEV' variable -- %s\n", + ncerr_to_str(err)); + res = RES_BAD_ARG; + goto error; + } + + /* Inquire the VLEV dimensionality */ + NC(inq_varndims(nc, varid, &ndims)); + if(ndims != NDIMS) { + fprintf(stderr, "The dimension of the 'VLEV' variable must be %i .\n", + NDIMS); + res = RES_BAD_ARG; + goto error; + } + NC(inq_vardimid(nc, varid, dimids)); + FOR_EACH(i, 0, NDIMS) NC(inq_dimlen(nc, dimids[i], len+i)); + + /* Inquire the type of the VLEV variable */ + NC(inq_vartype(nc, varid, &type)); + if(type != NC_DOUBLE && type != NC_FLOAT) { + fprintf(stderr, "The type of 'VLEV' variable must be float or double.\n"); + res = RES_BAD_ARG; + goto error; + } + + /* Allocate te memory space where a Z column of the VLEV variable is going to + * be copied */ + mem = mem_alloc(len[Z]*sizeof_nctype(type)); + if(!mem) { + fprintf(stderr, "Could not allocate memory for the 'VLEV' variable.\n"); + res = RES_MEM_ERR; + goto error; + } + + /* Get only one Z column */ + start[X] = 0; dim_len[X] = 1; + start[Y] = 0; dim_len[Y] = 1; + start[Z] = 0; dim_len[Z] = len[Z]; + NC(get_vara(nc, varid, start, dim_len, mem)); + + #define AT(Mem, Id) (type == NC_FLOAT ? ((float*)Mem)[Id] : ((double*)Mem)[Id]) + + /* Assume that the Z dimension starts from 0 */ + vxsz = AT(mem, 0) * 2.0; + if(vxsz <= 0) { + fprintf(stderr, "The 'VLEV' variable can't have negative or null values.\n"); + res = RES_BAD_ARG; + goto error; + } + + /* Check if the Z dimension is regular */ + FOR_EACH(z, 1, len[Z]) { + if(!eq_eps(AT(mem, z) - AT(mem, z-1), vxsz, 1.e-6)) break; + } + irregular = (z != len[Z]); + + /* Setup the size of the voxel */ + pvxsz = mem_alloc(sizeof(double) * (irregular ? len[Z] : 1)); + if(!pvxsz) { + fprintf(stderr, + "Could not allocate memory for the voxel size along the Z dimension.\n"); + res = RES_MEM_ERR; + goto error; + } + pvxsz[0] = vxsz; + + if(irregular) { + FOR_EACH(z, 1, len[Z]) { + pvxsz[z] = (AT(mem, z) - (AT(mem, z-1) + pvxsz[z-1]*0.5)) * 2.0; + if(pvxsz[z] <= 0) { + fprintf(stderr, + "The 'VLEV' variable can't have negative or null values.\n"); + res = RES_BAD_ARG; + goto error; + } + } + } + + /* Check that the others columns have the same voxel size */ + if(check) { + size_t ix, iy; + + mem2 = mem_alloc(len[Z]*sizeof_nctype(type)); + if(!mem2) { + fprintf(stderr, + "Could not allocate memory to check the 'VLEV' variable.\n"); + res = RES_MEM_ERR; + goto error; + } + + FOR_EACH(iy, 0, len[Y]) { + start[Y] = iy; + FOR_EACH(ix, 1, len[X]) { + start[X] = ix; + NC(get_vara(nc, varid, start, dim_len, mem2)); + FOR_EACH(z, 0, len[Z]) { + if(!eq_eps(AT(mem2, z), AT(mem, z), 1.e-6)) { + fprintf(stderr, + "The Z columns of the 'VLEV' variable must be the same.\n"); + res = RES_BAD_ARG; + goto error; + } + } + } + } + } + #undef AT + + *lower_pos = 0; + *voxel_size = pvxsz; + *is_z_irregular = (int8_t)irregular; + +exit: + if(mem2) mem_rm(mem2); + if(mem) mem_rm(mem); + return res; +error: + goto exit; +} + +static res_T +write_data(int nc, const char* var_name, FILE* stream) +{ + enum { TIME, Z, X, Y, NDIMS }; /* Helper constants */ + + char* mem = NULL; /* Memory where NetCDF data are copied */ + char* mem2 = NULL; /* Tmp memory where NetCDF data are converted */ + size_t start[NDIMS]; + size_t dim_len[NDIMS]; + size_t len[NDIMS]; + size_t slice_len; /* #elements per slice the 3D grid */ + size_t i; /* Common id */ + size_t t; /* Id over the time dimension */ + size_t z; /* Id over the Z dimension */ + nc_type type; + int varid; /* NetCDF id of the data to write */ + int ndims; /* #dimensions of the NetCDF data to write */ + int dimids[NDIMS]; /* NetCDF id of the data dimension */ + int err = NC_NOERR; + res_T res = RES_OK; + CHK(var_name); + + /* Retrieve the NetCDF id of the variable */ + err = nc_inq_varid(nc, var_name, &varid); + if(err != NC_NOERR) { + fprintf(stderr, "Could not inquire the '%s' variable -- %s\n", + var_name, ncerr_to_str(err)); + res = RES_BAD_ARG; + goto error; + } + + /* Inquire the dimensions of the variable */ + NC(inq_varndims(nc, varid, &ndims)); + if(ndims != NDIMS) { + fprintf(stderr, "The dimension of the '%s' variable must be %i .\n", + var_name, NDIMS); + res = RES_BAD_ARG; + goto error; + } + NC(inq_vardimid(nc, varid, dimids)); + FOR_EACH(i, 0, NDIMS) NC(inq_dimlen(nc, dimids[i], len+i)); + + /* Inquire the type of the variable */ + NC(inq_vartype(nc, varid, &type)); + if(type != NC_DOUBLE && type != NC_FLOAT) { + fprintf(stderr, + "The type of the '%s' variable cannot be %s. Expecting floating point data.\n", + var_name, nctype_to_str(type)); + res = RES_BAD_ARG; + goto error; + } + + /* Alloc the memory space where the variable data wille be copied. Note that, + * in order to reduce memory consumption, only one slice is read at a given + * time and Z position. */ + slice_len = len[X]*len[Y]; /* # elements per slice */ + mem = mem_alloc(slice_len*sizeof_nctype(type)); + + /* Allocate a temporary memory space to store the variable data converted in + * double precision if they are stored in single precision. */ + mem2 = type==NC_DOUBLE ? mem : mem_alloc(slice_len*sizeof_nctype(NC_DOUBLE)); + + if(!mem || !mem2) { + fprintf(stderr, "Could not allocate memory for the '%s' variable.\n", + var_name); + res = RES_MEM_ERR; + goto error; + } + + /* Read the whole slice */ + start[X] = 0; dim_len[X] = len[X]; + start[Y] = 0; dim_len[Y] = len[Y]; + + FOR_EACH(t, 0, len[TIME]) { + start[TIME] = t, dim_len[TIME] = 1; + FOR_EACH(z, 0, len[Z]) { + size_t n; + start[Z] = z; dim_len[Z] = 1; + + NC(get_vara(nc, varid, start, dim_len, mem)); /* Fetch the slice data */ + + /* In order to be compliant with the htgop fileformat, single precision + * data must be converted in double precision */ + if(type == NC_FLOAT) { + FOR_EACH(i, 0, slice_len) ((double*)mem2)[i] = ((float*)mem)[i]; + } + + /* Write data */ + n = fwrite(mem2, sizeof_nctype(NC_DOUBLE), slice_len, stream); + if(n != slice_len) { + fprintf(stderr, "Error writing data of the '%s' variable -- '%s'.\n", + var_name, strerror(errno)); + res = RES_IO_ERR; + goto error; + } + } + } + +exit: + if(mem2 != mem) mem_rm(mem2); + if(mem) mem_rm(mem); + return res; +error: + goto exit; +} + +/******************************************************************************* + * Program + ******************************************************************************/ +int +main(int argc, char** argv) +{ + FILE* stream = stdout; + struct grid grid = GRID_NULL; + struct args args = ARGS_DEFAULT; + int err = 0; + int err_nc = NC_NOERR; + int nc = INVALID_ID; + res_T res = RES_OK; + + res = args_init(&args, argc, argv); + if(res != RES_OK) goto error; + if(args.quit) goto exit; + + if(args.output) { + res = open_output_stream(args.output, args.force_overwrite, &stream); + if(res != RES_OK) goto error; + } + + err_nc = nc_open(args.input, NC_WRITE|NC_MMAP, &nc); + if(err_nc != NC_NOERR) { + fprintf(stderr, "Error opening file `%s' -- %s.\n", + args.input, ncerr_to_str(err_nc)); + goto exit; + } + + #define CALL(Func) { if(RES_OK != (res = Func)) goto error; } (void)0 + CALL(setup_definition(nc, &grid.nx, &grid.ny, &grid.nz, &grid.ntimes)); + CALL(setup_regular_dimension + (nc, "W_E_direction", &grid.vxsz_x, grid.lower+0, args.check)); + CALL(setup_regular_dimension + (nc, "S_N_direction", &grid.vxsz_y, grid.lower+1, args.check)); + CALL(setup_Z_dimension + (nc, &grid.vxsz_z, grid.lower+2, &grid.is_z_irregular, args.check)); + #undef CALL + + #define WRITE(Var, N, Name) { \ + if(fwrite((Var), sizeof(*(Var)), (N), stream) != (N)) { \ + fprintf(stderr, "Error writing the %s.\n", (Name)); \ + res = RES_IO_ERR; \ + goto error; \ + } \ + } (void)0 + if(!args.no_output) { + const int64_t pagesize = (int64_t)args.pagesize; + + WRITE(&pagesize, 1, "page size"); + WRITE(&grid.is_z_irregular, 1, "'irregular' Z boolean"); + WRITE(&grid.nx, 1, "X definition"); + WRITE(&grid.ny, 1, "Y definition"); + WRITE(&grid.nz, 1, "Z definition"); + WRITE(&grid.ntimes, 1, "time definition"); + WRITE(grid.lower, 3, "lower position"); + WRITE(&grid.vxsz_x, 1, "X voxel size"); + WRITE(&grid.vxsz_y, 1, "Y voxel size"); + WRITE(grid.vxsz_z, grid.is_z_irregular?(size_t)grid.nz:1, "Z voxel size(s)"); + /* padding */ + fseek(stream, ALIGN_SIZE(ftell(stream), (off_t)pagesize), SEEK_SET); + write_data(nc, "RVT", stream); + /* padding */ + fseek(stream, ALIGN_SIZE(ftell(stream), (off_t)pagesize), SEEK_SET); + write_data(nc, "RCT", stream); + /*padding */ + fseek(stream, ALIGN_SIZE(ftell(stream), (off_t)pagesize), SEEK_SET); + } + #undef WRITE + +exit: + grid_release(&grid); + if(nc != INVALID_ID) NC(close(nc)); + if(mem_allocated_size() != 0) { + fprintf(stderr, "Memory leaks: %lu Bytes\n", + (unsigned long)mem_allocated_size()); + err = -1; + } + return err; +error: + err = -1; + goto exit; +} + diff --git a/src/les2htcop.h.in b/src/les2htcp.h.in diff --git a/src/test_htcop.c b/src/test_htcop.c @@ -1,65 +0,0 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include "htcop.h" -#include "test_htcop_utils.h" - -#include <rsys/logger.h> - -static void -log_stream(const char* msg, void* ctx) -{ - ASSERT(msg); - (void)msg, (void)ctx; - printf("%s\n", msg); -} - -int -main(int argc, char** argv) -{ - struct mem_allocator allocator; - struct logger logger; - struct htcop* htcop = NULL; - (void)argc, (void)argv; - - CHK(htcop_create(NULL, NULL, 0, NULL) == RES_BAD_ARG); - CHK(htcop_create(NULL, NULL, 0, &htcop) == RES_OK); - - CHK(htcop_ref_get(NULL) == RES_BAD_ARG); - CHK(htcop_ref_get(htcop) == RES_OK); - CHK(htcop_ref_put(NULL) == RES_BAD_ARG); - CHK(htcop_ref_put(htcop) == RES_OK); - CHK(htcop_ref_put(htcop) == RES_OK); - - CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); - CHK(htcop_create(NULL, &allocator, 1, &htcop) == RES_OK); - CHK(htcop_ref_put(htcop) == RES_OK); - - CHK(logger_init(&allocator, &logger) == RES_OK); - logger_set_stream(&logger, LOG_OUTPUT, log_stream, NULL); - logger_set_stream(&logger, LOG_ERROR, log_stream, NULL); - logger_set_stream(&logger, LOG_WARNING, log_stream, NULL); - - CHK(htcop_create(&logger, &allocator, 0, &htcop) == RES_OK); - CHK(htcop_ref_put(htcop) == RES_OK); - CHK(htcop_create(&logger, NULL, 0, &htcop) == RES_OK); - CHK(htcop_ref_put(htcop) == RES_OK); - - logger_release(&logger); - check_memory_allocator(&allocator); - mem_shutdown_proxy_allocator(&allocator); - CHK(mem_allocated_size() == 0); - return 0; -} diff --git a/src/test_htcop_load.c b/src/test_htcop_load.c @@ -1,120 +0,0 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include "htcop.h" -#include "test_htcop_utils.h" - -#include <unistd.h> - -int -main(int argc, char** argv) -{ - int64_t pagesize; - struct htcop* htcop = NULL; - struct htcop_desc desc = HTCOP_DESC_NULL; - FILE* stream = NULL; - int8_t i8; - int32_t i32[4]; - double dbl[3]; - size_t i, n; - size_t x, y, z, t; - (void)argc, (void)argv; - - stream = tmpfile(); - - pagesize = (int64_t)sysconf(_SC_PAGESIZE); /* pagesize */ - CHK(fwrite(&pagesize, sizeof(pagesize), 1, stream) == 1); - i8 = 0; /* irregular Z */ - CHK(fwrite(&i8, sizeof(i8), 1, stream) == 1); - i32[0] = 2; /* Definition */ - i32[1] = 2; - i32[2] = 3; - i32[3] = 1; - CHK(fwrite(i32, sizeof(i32[0]), 4, stream) == 4); - dbl[0] = 0; /* Lower position */ - dbl[1] = 0; - dbl[2] = 0; - CHK(fwrite(dbl, sizeof(dbl[0]), 3, stream) == 3); - dbl[0] = 1; /* Voxel size */ - dbl[1] = 2; - dbl[2] = 3; - CHK(fwrite(dbl, sizeof(dbl[0]), 3, stream) == 3); - - fseek(stream, ALIGN_SIZE(ftell(stream), pagesize), SEEK_SET); /* padding */ - FOR_EACH(i, 0, i32[0]*i32[1]*i32[2]*i32[3]) { /* RVT */ - dbl[0] = (double)i; - CHK(fwrite(dbl, sizeof(dbl[0]), 1, stream) == 1); - } - - fseek(stream, ALIGN_SIZE(ftell(stream), pagesize), SEEK_SET); /* padding */ - FOR_EACH(i, 0, i32[0]*i32[1]*i32[2]*i32[3]) { /* RCT */ - dbl[0] =-(double)i; - CHK(fwrite(dbl, sizeof(dbl[0]), 1, stream) == 1); - } - fseek(stream, ALIGN_SIZE(ftell(stream), pagesize), SEEK_SET); /* padding */ - - rewind(stream); - - CHK(htcop_create(NULL, &mem_default_allocator, 1, &htcop) == RES_OK); - - CHK(htcop_get_desc(NULL, &desc) == RES_BAD_ARG); - CHK(htcop_get_desc(htcop, NULL) == RES_BAD_ARG); - CHK(htcop_get_desc(htcop, &desc) == RES_BAD_ARG); - - CHK(htcop_load_stream(NULL, stream) == RES_BAD_ARG); - CHK(htcop_load_stream(htcop, NULL) == RES_BAD_ARG); - CHK(htcop_load_stream(htcop, stream) == RES_OK); - fclose(stream); - - CHK(htcop_get_desc(NULL, &desc) == RES_BAD_ARG); - CHK(htcop_get_desc(htcop, NULL) == RES_BAD_ARG); - CHK(htcop_get_desc(htcop, &desc) == RES_OK); - - CHK(desc.pagesize == 4096); - CHK(desc.irregular_z == 0); - CHK(desc.spatial_definition[0] == 2); - CHK(desc.spatial_definition[1] == 2); - CHK(desc.spatial_definition[2] == 3); - CHK(desc.time_definition == 1); - CHK(desc.lower[0] == 0); - CHK(desc.lower[1] == 0); - CHK(desc.lower[2] == 0); - CHK(desc.vxsz_x == 1); - CHK(desc.vxsz_y == 2); - CHK(desc.vxsz_z[0] == 3); - - n = desc.spatial_definition[0] - * desc.spatial_definition[1] - * desc.spatial_definition[2] - * desc.time_definition; - FOR_EACH(i, 0,n) { - CHK(desc.RVT[i] == (double)i); - CHK(desc.RCT[i] ==-(double)i); - } - i = 0; - FOR_EACH(t, 0, desc.time_definition) { - FOR_EACH(z, 0, desc.spatial_definition[2]) { - FOR_EACH(y, 0, desc.spatial_definition[1]) { - FOR_EACH(x, 0, desc.spatial_definition[0]) { - CHK(htcop_desc_RVT_at(&desc, x, y, z, t) == (double)i); - CHK(htcop_desc_RCT_at(&desc, x, y, z, t) ==-(double)i); - ++i; - }}}} - CHK(htcop_ref_put(htcop) == RES_OK); - - CHK(mem_allocated_size() == 0); - return 0; -} - diff --git a/src/test_htcop_load_from_file.c b/src/test_htcop_load_from_file.c @@ -1,75 +0,0 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#include "htcop.h" -#include "test_htcop_utils.h" - -int -main(int argc, char** argv) -{ - struct htcop* htcop = NULL; - struct htcop_desc desc = HTCOP_DESC_NULL; - size_t i, n; - - if(argc < 2) { - fprintf(stderr, "Usage: %s HTCOP_FILE\n", argv[0]); - return -1; - } - - CHK(htcop_create(NULL, &mem_default_allocator, 1, &htcop) == RES_OK); - CHK(htcop_load(htcop, argv[1]) == RES_OK); - - CHK(htcop_get_desc(htcop, &desc) == RES_OK); - - printf("pagesize: %lu\n", (unsigned long)desc.pagesize); - printf("irregular Z: %i\n", desc.irregular_z); - printf("#X: %lu; #Y: %lu; #Z: %lu; #times: %lu\n", - desc.spatial_definition[0], - desc.spatial_definition[1], - desc.spatial_definition[2], - desc.time_definition); - printf("lower pos: %g %g %g\n", SPLIT3(desc.lower)); - printf("voxel size: %g %g ", desc.vxsz_x, desc.vxsz_y); - if(!desc.irregular_z) { - printf("%g\n", desc.vxsz_z[0]); - } else { - printf("{"); - FOR_EACH(i, 0, desc.spatial_definition[2]) { - printf("%g", desc.vxsz_z[i]); - if(i != desc.spatial_definition[2]-1) printf(", "); - } - printf("}"); - } - - n = desc.spatial_definition[0] - * desc.spatial_definition[1] - * desc.spatial_definition[2] - * desc.time_definition; - - printf("RVT:\n"); - FOR_EACH(i, 0, n) { - printf("%g\n", desc.RVT[i]); - } - - printf("RCT:\n"); - FOR_EACH(i, 0, n) { - printf("%g\n", desc.RCT[i]); - } - - CHK(htcop_ref_put(htcop) == RES_OK); - check_memory_allocator(&mem_default_allocator); - CHK(mem_allocated_size() == 0); - return 0; -} diff --git a/src/test_htcop_utils.h b/src/test_htcop_utils.h @@ -1,34 +0,0 @@ -/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - -#ifndef TEST_HTCOP_UTILS_H -#define TEST_HTCOP_UTILS_H - -#include <rsys/mem_allocator.h> -#include <stdio.h> - -static INLINE void -check_memory_allocator(struct mem_allocator* allocator) -{ - if(MEM_ALLOCATED_SIZE(allocator)) { - char dump[512]; - MEM_DUMP(allocator, dump, sizeof(dump)/sizeof(char)); - fprintf(stderr, "%s\n", dump); - FATAL("Memory leaks\n"); - } -} - -#endif /* TEST_HTCOP_UTILS_H */ - diff --git a/src/test_htcp.c b/src/test_htcp.c @@ -0,0 +1,65 @@ +/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "htcp.h" +#include "test_htcp_utils.h" + +#include <rsys/logger.h> + +static void +log_stream(const char* msg, void* ctx) +{ + ASSERT(msg); + (void)msg, (void)ctx; + printf("%s\n", msg); +} + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct logger logger; + struct htcp* htcp = NULL; + (void)argc, (void)argv; + + CHK(htcp_create(NULL, NULL, 0, NULL) == RES_BAD_ARG); + CHK(htcp_create(NULL, NULL, 0, &htcp) == RES_OK); + + CHK(htcp_ref_get(NULL) == RES_BAD_ARG); + CHK(htcp_ref_get(htcp) == RES_OK); + CHK(htcp_ref_put(NULL) == RES_BAD_ARG); + CHK(htcp_ref_put(htcp) == RES_OK); + CHK(htcp_ref_put(htcp) == RES_OK); + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(htcp_create(NULL, &allocator, 1, &htcp) == RES_OK); + CHK(htcp_ref_put(htcp) == RES_OK); + + CHK(logger_init(&allocator, &logger) == RES_OK); + logger_set_stream(&logger, LOG_OUTPUT, log_stream, NULL); + logger_set_stream(&logger, LOG_ERROR, log_stream, NULL); + logger_set_stream(&logger, LOG_WARNING, log_stream, NULL); + + CHK(htcp_create(&logger, &allocator, 0, &htcp) == RES_OK); + CHK(htcp_ref_put(htcp) == RES_OK); + CHK(htcp_create(&logger, NULL, 0, &htcp) == RES_OK); + CHK(htcp_ref_put(htcp) == RES_OK); + + logger_release(&logger); + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_htcp_load.c b/src/test_htcp_load.c @@ -0,0 +1,120 @@ +/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "htcp.h" +#include "test_htcp_utils.h" + +#include <unistd.h> + +int +main(int argc, char** argv) +{ + int64_t pagesize; + struct htcp* htcp = NULL; + struct htcp_desc desc = HTCP_DESC_NULL; + FILE* stream = NULL; + int8_t i8; + int32_t i32[4]; + double dbl[3]; + size_t i, n; + size_t x, y, z, t; + (void)argc, (void)argv; + + stream = tmpfile(); + + pagesize = (int64_t)sysconf(_SC_PAGESIZE); /* pagesize */ + CHK(fwrite(&pagesize, sizeof(pagesize), 1, stream) == 1); + i8 = 0; /* irregular Z */ + CHK(fwrite(&i8, sizeof(i8), 1, stream) == 1); + i32[0] = 2; /* Definition */ + i32[1] = 2; + i32[2] = 3; + i32[3] = 1; + CHK(fwrite(i32, sizeof(i32[0]), 4, stream) == 4); + dbl[0] = 0; /* Lower position */ + dbl[1] = 0; + dbl[2] = 0; + CHK(fwrite(dbl, sizeof(dbl[0]), 3, stream) == 3); + dbl[0] = 1; /* Voxel size */ + dbl[1] = 2; + dbl[2] = 3; + CHK(fwrite(dbl, sizeof(dbl[0]), 3, stream) == 3); + + fseek(stream, ALIGN_SIZE(ftell(stream), pagesize), SEEK_SET); /* padding */ + FOR_EACH(i, 0, i32[0]*i32[1]*i32[2]*i32[3]) { /* RVT */ + dbl[0] = (double)i; + CHK(fwrite(dbl, sizeof(dbl[0]), 1, stream) == 1); + } + + fseek(stream, ALIGN_SIZE(ftell(stream), pagesize), SEEK_SET); /* padding */ + FOR_EACH(i, 0, i32[0]*i32[1]*i32[2]*i32[3]) { /* RCT */ + dbl[0] =-(double)i; + CHK(fwrite(dbl, sizeof(dbl[0]), 1, stream) == 1); + } + fseek(stream, ALIGN_SIZE(ftell(stream), pagesize), SEEK_SET); /* padding */ + + rewind(stream); + + CHK(htcp_create(NULL, &mem_default_allocator, 1, &htcp) == RES_OK); + + CHK(htcp_get_desc(NULL, &desc) == RES_BAD_ARG); + CHK(htcp_get_desc(htcp, NULL) == RES_BAD_ARG); + CHK(htcp_get_desc(htcp, &desc) == RES_BAD_ARG); + + CHK(htcp_load_stream(NULL, stream) == RES_BAD_ARG); + CHK(htcp_load_stream(htcp, NULL) == RES_BAD_ARG); + CHK(htcp_load_stream(htcp, stream) == RES_OK); + fclose(stream); + + CHK(htcp_get_desc(NULL, &desc) == RES_BAD_ARG); + CHK(htcp_get_desc(htcp, NULL) == RES_BAD_ARG); + CHK(htcp_get_desc(htcp, &desc) == RES_OK); + + CHK(desc.pagesize == 4096); + CHK(desc.irregular_z == 0); + CHK(desc.spatial_definition[0] == 2); + CHK(desc.spatial_definition[1] == 2); + CHK(desc.spatial_definition[2] == 3); + CHK(desc.time_definition == 1); + CHK(desc.lower[0] == 0); + CHK(desc.lower[1] == 0); + CHK(desc.lower[2] == 0); + CHK(desc.vxsz_x == 1); + CHK(desc.vxsz_y == 2); + CHK(desc.vxsz_z[0] == 3); + + n = desc.spatial_definition[0] + * desc.spatial_definition[1] + * desc.spatial_definition[2] + * desc.time_definition; + FOR_EACH(i, 0,n) { + CHK(desc.RVT[i] == (double)i); + CHK(desc.RCT[i] ==-(double)i); + } + i = 0; + FOR_EACH(t, 0, desc.time_definition) { + FOR_EACH(z, 0, desc.spatial_definition[2]) { + FOR_EACH(y, 0, desc.spatial_definition[1]) { + FOR_EACH(x, 0, desc.spatial_definition[0]) { + CHK(htcp_desc_RVT_at(&desc, x, y, z, t) == (double)i); + CHK(htcp_desc_RCT_at(&desc, x, y, z, t) ==-(double)i); + ++i; + }}}} + CHK(htcp_ref_put(htcp) == RES_OK); + + CHK(mem_allocated_size() == 0); + return 0; +} + diff --git a/src/test_htcp_load_from_file.c b/src/test_htcp_load_from_file.c @@ -0,0 +1,75 @@ +/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "htcp.h" +#include "test_htcp_utils.h" + +int +main(int argc, char** argv) +{ + struct htcp* htcp = NULL; + struct htcp_desc desc = HTCP_DESC_NULL; + size_t i, n; + + if(argc < 2) { + fprintf(stderr, "Usage: %s HTCP_FILE\n", argv[0]); + return -1; + } + + CHK(htcp_create(NULL, &mem_default_allocator, 1, &htcp) == RES_OK); + CHK(htcp_load(htcp, argv[1]) == RES_OK); + + CHK(htcp_get_desc(htcp, &desc) == RES_OK); + + printf("pagesize: %lu\n", (unsigned long)desc.pagesize); + printf("irregular Z: %i\n", desc.irregular_z); + printf("#X: %lu; #Y: %lu; #Z: %lu; #times: %lu\n", + desc.spatial_definition[0], + desc.spatial_definition[1], + desc.spatial_definition[2], + desc.time_definition); + printf("lower pos: %g %g %g\n", SPLIT3(desc.lower)); + printf("voxel size: %g %g ", desc.vxsz_x, desc.vxsz_y); + if(!desc.irregular_z) { + printf("%g\n", desc.vxsz_z[0]); + } else { + printf("{"); + FOR_EACH(i, 0, desc.spatial_definition[2]) { + printf("%g", desc.vxsz_z[i]); + if(i != desc.spatial_definition[2]-1) printf(", "); + } + printf("}"); + } + + n = desc.spatial_definition[0] + * desc.spatial_definition[1] + * desc.spatial_definition[2] + * desc.time_definition; + + printf("RVT:\n"); + FOR_EACH(i, 0, n) { + printf("%g\n", desc.RVT[i]); + } + + printf("RCT:\n"); + FOR_EACH(i, 0, n) { + printf("%g\n", desc.RCT[i]); + } + + CHK(htcp_ref_put(htcp) == RES_OK); + check_memory_allocator(&mem_default_allocator); + CHK(mem_allocated_size() == 0); + return 0; +} diff --git a/src/test_htcp_utils.h b/src/test_htcp_utils.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2018 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef TEST_HTCP_UTILS_H +#define TEST_HTCP_UTILS_H + +#include <rsys/mem_allocator.h> +#include <stdio.h> + +static INLINE void +check_memory_allocator(struct mem_allocator* allocator) +{ + if(MEM_ALLOCATED_SIZE(allocator)) { + char dump[512]; + MEM_DUMP(allocator, dump, sizeof(dump)/sizeof(char)); + fprintf(stderr, "%s\n", dump); + FATAL("Memory leaks\n"); + } +} + +#endif /* TEST_HTCP_UTILS_H */ +