2026-03-28 16:54:11 +11:00
# pragma once
# ifndef UTILITY_H
# define UTILITY_H
# include "ANSLicense.h"
# include <boost/date_time.hpp>
# include <boost/date_time/gregorian/gregorian.hpp>
# include "boost/property_tree/ptree.hpp"
# include "boost/property_tree/json_parser.hpp"
# include "boost/foreach.hpp"
# include <zlib.h>
# include <zip.h>
# include <filesystem>
# include <cstring>
# include <iostream>
# include <vector>
# include <regex>
# include <stdio.h>
2026-04-24 11:29:28 +10:00
# include <memory>
# include <mutex>
2026-03-28 16:54:11 +11:00
//namespace logging = boost::log;
//namespace attrs = boost::log::attributes;
//namespace expr = boost::log::expressions;
//namespace src = boost::log::sources;
//namespace keywords = boost::log::keywords;
//namespace sinks = boost::log::sinks;
namespace fs = std : : filesystem ;
# ifndef MIN
# define MIN(a,b) ((a) > (b) ? (b) : (a))
# endif
# ifndef MAX
# define MAX(a,b) ((a) < (b) ? (b) : (a))
# endif
# define DETECT_BUFFER_SIZE 0x9000
ANSLICENSE_API std : : string path_to_filename ( std : : string path ) ;
ANSLICENSE_API std : : string GetMainFolderDir ( ) ;
ANSLICENSE_API std : : string GetLoggerDir ( ) ;
ANSLICENSE_API std : : string GetLicenseDir ( ) ;
// Convert file path to only the filename
ANSLICENSE_API std : : wstring String2WString ( const std : : string input ) ;
ANSLICENSE_API std : : string WString2String ( const std : : wstring input ) ;
ANSLICENSE_API std : : string Dash2Underscore ( std : : string text ) ;
ANSLICENSE_API std : : string Underscore2Dash ( std : : string text ) ;
ANSLICENSE_API std : : string Space2Underscore ( std : : string text ) ;
[ [ nodiscard ] ] ANSLICENSE_API bool FileExist ( const std : : string & filePath ) ;
[ [ nodiscard ] ] ANSLICENSE_API bool FolderExist ( const std : : string & folderPath ) ;
[ [ nodiscard ] ] ANSLICENSE_API bool CreateDirectory ( const std : : string & folderPath ) ;
[ [ nodiscard ] ] ANSLICENSE_API bool RenameFile ( const std : : string & oldFilePath , const std : : string & newFilePath ) ;
[ [ nodiscard ] ] ANSLICENSE_API bool CopyFile ( const std : : string & sourceFilePath , const std : : string & destinationFilePath ) ;
ANSLICENSE_API void CopyFiles ( const std : : string & sourceFolder , const std : : string & destinationFolder ) ;
[ [ nodiscard ] ] ANSLICENSE_API bool DeleteFile ( const std : : string & filePath ) ;
[ [ nodiscard ] ] ANSLICENSE_API bool DeleteLicenseFile ( const std : : string & filePath ) ;
[ [ nodiscard ] ] ANSLICENSE_API std : : vector < std : : string > ListFilesInFolder ( const std : : string & folderPath ) ;
ANSLICENSE_API void DeleteFilesInFolderExcept ( const std : : string & folderPath , const std : : vector < std : : string > & filesToKeep ) ;
[ [ nodiscard ] ] ANSLICENSE_API bool DeleteFolder ( const std : : string & folderPath ) ;
ANSLICENSE_API void ToLowerCase ( std : : string & str ) ;
ANSLICENSE_API std : : string toLower ( const std : : string & str ) ;
ANSLICENSE_API std : : string GetFileExtension ( const std : : string & filePath ) ;
ANSLICENSE_API std : : string GetFileNameWithoutExtension ( const std : : string & filePath ) ;
ANSLICENSE_API std : : string FindFilePathInFileList ( const std : : vector < std : : string > & fileList , const std : : string & fileName ) ;
ANSLICENSE_API std : : string ReadFileContent ( std : : string filePath ) ;
ANSLICENSE_API std : : string ReadFileAsBinary ( const std : : string & filename ) ;
ANSLICENSE_API int ReadAllBytes ( std : : string filePath , std : : vector < char > & allDataBytes ) ;
ANSLICENSE_API std : : string ExtractFolderName ( const std : : string & filePath ) ; // Function to extract the folder name from a file path or file name
ANSLICENSE_API std : : string GetParentFolder ( const std : : string & directoryPath ) ; // Function to extract the folder name from a file path or file name
ANSLICENSE_API std : : string CreateFilePath ( const std : : string & folderName , const std : : string & fileName ) ; // Function to create a file path from a folder name and a file name
ANSLICENSE_API std : : filesystem : : path GetSafeTempDirectory ( ) ;
ANSLICENSE_API std : : string GenerateOutputFolder ( std : : string modelFilePath , std : : string modelName , bool edgeDeviceModel = true ) ;
ANSLICENSE_API bool ExtractPasswordProtectedZip ( const std : : string & zipFileName , const std : : string & password , const std : : string & modelName , std : : string & outputFolder , bool edgeDeviceModel = true ) ;
ANSLICENSE_API bool AddFileToZip ( zip * archive , const char * filePath , const char * zipPath , const char * password ) ;
ANSLICENSE_API bool AddFolderContentsToZip ( zip * archive , const char * folderPath , const char * zipPath , const char * password ) ;
ANSLICENSE_API bool ZipFolderWithPassword ( const char * folderPath , const char * zipFilePath , const char * password ) ;
ANSLICENSE_API std : : string VectorToCommaSeparatedString ( const std : : vector < std : : string > & inputVector ) ;
ANSLICENSE_API std : : string VectorToSeparatedString ( const std : : vector < std : : string > & inputVector ) ;
ANSLICENSE_API std : : vector < std : : string > Split ( const std : : string str , const std : : string regex_str ) ;
ANSLICENSE_API bool ReadLicenseKeyFile ( std : : string licenseKeyFilePath , std : : string & licenseKey , std : : string & activationKey , std : : string & validationData ) ;
ANSLICENSE_API std : : vector < std : : string > GetTrialLicenseKeyFiles ( std : : string registrationName ) ;
ANSLICENSE_API std : : string GetTrialLicenseKey ( std : : string registrationName ) ;
ANSLICENSE_API bool UpdateTrialLicenseKey ( std : : string trialLicenseKey , std : : string registrationName ) ;
ANSLICENSE_API int GetExpirationDate ( std : : string licenseData , int & productId ) ;
ANSLICENSE_API bool RemoveTrialLicenseKey ( std : : string registrationName ) ;
ANSLICENSE_API std : : string GetStringCurrentDateTime ( ) ; // Return current datetime in string format
ANSLICENSE_API std : : string GetDateTimeString ( const std : : string & format ) ;
// For training engine
//bool ExtractPasswordProtectedZipForTrainingEgnine(const std::string& zipFileName, const std::string& password, const std::string& modelName, std::string& outputFolder, bool edgeDeviceModel = true);
ANSLICENSE_API bool ExtractProtectedZipFile ( const std : : string & zipFileName , const std : : string & password , const std : : string & modelName , const std : : string outputFolder ) ;
2026-04-24 11:29:28 +10:00
// Per-path mutex for a model folder. Used to serialize extract ↔ session
// creation on the same extracted folder so concurrent CreateANSODHandle calls
// cannot truncate/rewrite a model file while another thread is loading it.
// Keyed by folder path (not zip path) so both extractor and consumer agree.
2026-04-24 12:54:16 +10:00
// Returns std::recursive_timed_mutex so callers can bound their wait and
// recursion — layered load paths (e.g. ANSALPR_OD::LoadEngine ->
// ANSONNXYOLO::LoadModelFromFolder) legitimately re-enter on the same
// thread; a non-recursive timed_mutex self-deadlocks that nesting. Cross-
// thread serialization is unchanged.
ANSLICENSE_API std : : shared_ptr < std : : recursive_timed_mutex > GetModelFolderLock ( const std : : string & folderPath ) ;
2026-04-24 12:19:54 +10:00
// ============================================================================
// ModelFolderLockGuard
//
// RAII guard that serializes "open files in an extracted model folder" against
// re-entries into ExtractProtectedZipFile on the SAME folder. Without this,
// two threads creating handles for the same model zip can race so that thread
// A opens a model file (via ORT / TRT / OpenVINO) while thread B truncates it
// via std::ofstream inside the extractor — Windows surfaces this as "system
// error number 13" (EACCES) on the reader.
//
// Backed by GetModelFolderLock() above which returns a process-wide
2026-04-24 12:54:16 +10:00
// std::recursive_timed_mutex keyed on the folder path. The extractor takes
// the same lock, so extract ↔ open is mutually exclusive across threads,
// while same-thread re-entry (layered loaders) is permitted without
// deadlocking.
2026-04-24 12:19:54 +10:00
//
// Acquisition is bounded by `timeout` (default 120 s) so a deadlocked peer
// cannot hang the caller thread forever. On timeout, .acquired() is false and
// the caller must fail the load.
//
// Lives in Utility.h (rather than a per-module header) so every ANSCORE
// module — ANSODEngine, ANSOCR, ANSLPR, ANSFR, etc. — can use it without
// pulling in ANSODEngine headers.
//
// Usage:
//
// bool MyEngine::LoadModelFromFolder(...) {
// bool result = MyBase::LoadModelFromFolder(...);
// if (!result) return false;
// // ── serialize derived-class init against concurrent extracts ──
// ANSCENTER::ModelFolderLockGuard _flg(_modelFolder,
// "MyEngine::LoadModelFromFolder");
// if (!_flg.acquired()) {
// _logger.LogError("MyEngine::LoadModelFromFolder",
// "Timed out waiting for model-folder lock: " + _modelFolder,
// __FILE__, __LINE__);
// return false;
// }
// // ... existing body: Init(...) / buildLoadNetwork(...) / etc. ...
// }
//
// Notes:
// • Placement — insert AFTER the base's XXX() returns (extractor already
// released its own lock by then) and BEFORE any file open in _modelFolder.
// Wrapping the base call would deadlock — it takes the same lock itself.
// • RAII — destructor auto-releases on every return path within the scope.
// • Timing — entry/acquire/timeout are traced via ANS_DBG("EngineLoad"),
// filter on [EngineLoad] in DebugView to diagnose stalls.
// ============================================================================
namespace ANSCENTER {
class ModelFolderLockGuard {
public :
explicit ModelFolderLockGuard ( const std : : string & folderPath ,
const char * caller ,
std : : chrono : : seconds timeout = std : : chrono : : seconds ( 120 ) )
: _caller ( caller ? caller : " (?) " ) , _folder ( folderPath )
{
if ( folderPath . empty ( ) ) {
// Nothing to serialize — no extracted folder yet. Treat as
// acquired so caller's existing init runs unchanged (e.g.
// custom-path engines that never go through the zip extractor).
_ok = true ;
ANS_DBG ( " EngineLoad " , " %s: empty folder path, skipping lock " ,
_caller ) ;
return ;
}
auto lock = GetModelFolderLock ( folderPath ) ;
2026-04-24 12:54:16 +10:00
_guard = std : : unique_lock < std : : recursive_timed_mutex > ( * lock , std : : defer_lock ) ;
2026-04-24 12:19:54 +10:00
ANS_DBG ( " EngineLoad " ,
" %s: waiting on folder lock (%llds): %s " ,
_caller , ( long long ) timeout . count ( ) , folderPath . c_str ( ) ) ;
const auto t0 = std : : chrono : : steady_clock : : now ( ) ;
if ( _guard . try_lock_for ( timeout ) ) {
const auto ms = std : : chrono : : duration_cast < std : : chrono : : milliseconds > (
std : : chrono : : steady_clock : : now ( ) - t0 ) . count ( ) ;
_ok = true ;
ANS_DBG ( " EngineLoad " , " %s: folder lock acquired in %lldms " ,
_caller , ( long long ) ms ) ;
} else {
const auto ms = std : : chrono : : duration_cast < std : : chrono : : milliseconds > (
std : : chrono : : steady_clock : : now ( ) - t0 ) . count ( ) ;
_ok = false ;
ANS_DBG ( " EngineLoad " ,
" %s: TIMEOUT on folder lock after %lldms: %s " ,
_caller , ( long long ) ms , folderPath . c_str ( ) ) ;
}
}
// Non-copyable, non-movable: lock lifetime is tied to this scope.
ModelFolderLockGuard ( const ModelFolderLockGuard & ) = delete ;
ModelFolderLockGuard & operator = ( const ModelFolderLockGuard & ) = delete ;
bool acquired ( ) const noexcept { return _ok ; }
explicit operator bool ( ) const noexcept { return _ok ; }
private :
const char * _caller ;
std : : string _folder ;
2026-04-24 12:54:16 +10:00
std : : unique_lock < std : : recursive_timed_mutex > _guard ;
2026-04-24 12:19:54 +10:00
bool _ok = false ;
} ;
} // namespace ANSCENTER
2026-03-28 16:54:11 +11:00
# endif