aqnwb 0.3.0
Loading...
Searching...
No Matches
Utils.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <chrono>
6#include <cmath>
7#include <cstdint>
8#include <ctime>
9#include <iomanip>
10#include <random>
11#include <regex>
12#include <sstream>
13#include <string>
14
15#include "io/BaseIO.hpp"
16#include "io/hdf5/HDF5IO.hpp"
17
18namespace AQNWB
19{
20namespace detail
21{
27inline std::tm to_local_time(std::time_t time_value)
28{
29 std::tm local_tm {};
30#if defined(_WIN32)
31 localtime_s(&local_tm, &time_value);
32#elif defined(__unix__) || defined(__APPLE__)
33 localtime_r(&time_value, &local_tm);
34#else
35 const std::tm* local_tm_ptr = std::localtime(&time_value);
36 if (local_tm_ptr) {
37 local_tm = *local_tm_ptr;
38 }
39#endif
40 return local_tm;
41}
42
48inline std::tm to_utc_time(std::time_t time_value)
49{
50 std::tm utc_tm {};
51#if defined(_WIN32)
52 gmtime_s(&utc_tm, &time_value);
53#elif defined(__unix__) || defined(__APPLE__)
54 gmtime_r(&time_value, &utc_tm);
55#else
56 const std::tm* utc_tm_ptr = std::gmtime(&time_value);
57 if (utc_tm_ptr) {
58 utc_tm = *utc_tm_ptr;
59 }
60#endif
61 return utc_tm;
62}
63
69inline long get_utc_offset_seconds(std::time_t time_value)
70{
71 std::tm local_tm = to_local_time(time_value);
72 std::tm utc_tm = to_utc_time(time_value);
73
74 std::time_t local_time = std::mktime(&local_tm);
75 std::time_t utc_time = std::mktime(&utc_tm);
76 if (local_time == static_cast<std::time_t>(-1)
77 || utc_time == static_cast<std::time_t>(-1))
78 {
79 return 0;
80 }
81 return static_cast<long>(std::difftime(local_time, utc_time));
82}
83
89inline std::string format_utc_offset(long offset_seconds)
90{
91 const char sign = (offset_seconds < 0) ? '-' : '+';
92 long abs_offset = (offset_seconds < 0) ? -offset_seconds : offset_seconds;
93 long hours = abs_offset / 3600;
94 long minutes = (abs_offset % 3600) / 60;
95
96 std::ostringstream oss;
97 oss << sign << std::setw(2) << std::setfill('0') << hours << ':'
98 << std::setw(2) << std::setfill('0') << minutes;
99 return oss.str();
100}
101
107inline uint16_t to_little_endian_u16(uint16_t value)
108{
109#if defined(_WIN32) \
110 || (defined(__BYTE_ORDER__) \
111 && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
112 return value;
113#else
114 return static_cast<uint16_t>((value >> 8) | (value << 8));
115#endif
116}
117} // namespace detail
118
123static inline std::string generateUuid()
124{
125 std::array<uint8_t, 16> bytes {};
126 std::random_device rd;
127 std::mt19937 gen(rd());
128 std::uniform_int_distribution<uint32_t> dist(0, 0xFFFFFFFF);
129
130 for (size_t i = 0; i < bytes.size(); i += 4) {
131 uint32_t random_value = dist(gen);
132 bytes[i] = static_cast<uint8_t>(random_value & 0xFF);
133 bytes[i + 1] = static_cast<uint8_t>((random_value >> 8) & 0xFF);
134 bytes[i + 2] = static_cast<uint8_t>((random_value >> 16) & 0xFF);
135 bytes[i + 3] = static_cast<uint8_t>((random_value >> 24) & 0xFF);
136 }
137
138 // RFC 4122 version 4 UUID.
139 bytes[6] = static_cast<uint8_t>((bytes[6] & 0x0F) | 0x40);
140 bytes[8] = static_cast<uint8_t>((bytes[8] & 0x3F) | 0x80);
141
142 std::ostringstream oss;
143 oss << std::hex << std::nouppercase << std::setfill('0');
144 for (size_t i = 0; i < bytes.size(); ++i) {
145 if (i == 4 || i == 6 || i == 8 || i == 10) {
146 oss << '-';
147 }
148 oss << std::setw(2) << static_cast<int>(bytes[i]);
149 }
150 return oss.str();
151}
152
157static inline std::string getCurrentTime()
158{
159 auto now = std::chrono::system_clock::now();
160 auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(now);
161 auto micros =
162 std::chrono::duration_cast<std::chrono::microseconds>(now - seconds)
163 .count();
164 std::time_t time_value = std::chrono::system_clock::to_time_t(seconds);
165 std::tm local_tm = detail::to_local_time(time_value);
166 long offset_seconds = detail::get_utc_offset_seconds(time_value);
167
168 std::ostringstream oss;
169 oss << std::put_time(&local_tm, "%Y-%m-%dT%H:%M:%S");
170 oss << '.' << std::setw(6) << std::setfill('0') << micros;
171 oss << detail::format_utc_offset(offset_seconds);
172 return oss.str();
173}
174
183static inline bool isISO8601Date(const std::string& dateStr)
184{
185 // Define the regex pattern for ISO 8601 extended format with timezone offset
186 // Allow one or more fractional seconds digits
187 const std::string iso8601Pattern =
188 R"(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+[+-]\d{2}:\d{2}$)";
189 std::regex pattern(iso8601Pattern);
190
191 // Check if the date string matches the regex pattern
192 return std::regex_match(dateStr, pattern);
193}
194
202static inline std::shared_ptr<IO::BaseIO> createIO(const std::string& type,
203 const std::string& filename)
204{
205 if (type == "HDF5") {
206 return std::make_shared<AQNWB::IO::HDF5::HDF5IO>(filename);
207 } else {
208 throw std::invalid_argument("Invalid IO type");
209 }
210}
211
225static inline std::string mergePaths(const std::string& path1,
226 const std::string& path2)
227{
228 std::string result = path1;
229 // Remove trailing "/" from path1
230 while (!result.empty() && result.back() == '/' && result != "/") {
231 result.pop_back();
232 }
233 // Remove leading "/" from path2
234 size_t start = 0;
235 while (start < path2.size() && path2[start] == '/') {
236 start++;
237 }
238 // Get path2 without trailing slashes
239 std::string path2Clean = path2.substr(start);
240 while (!path2Clean.empty() && path2Clean.back() == '/' && path2Clean != "/") {
241 path2Clean.pop_back();
242 }
243 // Append path2 to path1 with a "/" in between
244 if (!result.empty() && !path2Clean.empty()) {
245 result += '/';
246 }
247 result += path2Clean;
248
249 // Remove any potential occurrences of "//" and replace with "/"
250 size_t pos = result.find("//");
251 while (pos != std::string::npos) {
252 result.replace(pos, 2, "/");
253 pos = result.find("//", pos);
254 }
255
256 // Remove trailing "/" from final result if not root path
257 while (!result.empty() && result.back() == '/' && result != "/") {
258 result.pop_back();
259 }
260
261 return result;
262}
263
272static inline void convertFloatToInt16LE(const float* source,
273 void* dest,
274 SizeType numSamples)
275{
276 // TODO - several steps in this function may be unnecessary for our use
277 // case. Consider simplifying the intermediate cast to char and the
278 // final cast to uint16_t.
279 auto maxVal = static_cast<double>(0x7fff);
280 auto intData = static_cast<char*>(dest);
281
282 for (SizeType i = 0; i < numSamples; ++i) {
283 auto clampedValue =
284 std::clamp(maxVal * static_cast<double>(source[i]), -maxVal, maxVal);
285 auto intValue =
286 static_cast<uint16_t>(static_cast<int16_t>(std::round(clampedValue)));
287 intValue = detail::to_little_endian_u16(intValue);
288 *reinterpret_cast<uint16_t*>(intData) = intValue;
289 intData += 2; // destBytesPerSample is always 2
290 }
291}
292
299static inline std::unique_ptr<int16_t[]> transformToInt16(
300 SizeType numSamples, float conversion_factor, const float* data)
301{
302 std::unique_ptr<float[]> scaledData = std::make_unique<float[]>(numSamples);
303 std::unique_ptr<int16_t[]> intData = std::make_unique<int16_t[]>(numSamples);
304
305 // copy data and multiply by scaling factor
306 float multFactor = 1.0f / (32767.0f * conversion_factor);
307 std::transform(data,
308 data + numSamples,
309 scaledData.get(),
310 [multFactor](float value) { return value * multFactor; });
311
312 // convert float to int16
313 convertFloatToInt16LE(scaledData.get(), intData.get(), numSamples);
314
315 return intData;
316}
317
323static inline bool isValidIndex(SizeType index)
324{
325 return (index != AQNWB::Types::SizeTypeNotSet);
326}
327
334static inline Status intToStatus(int status)
335{
336 return (status < 0) ? Status::Failure : Status::Success;
337}
338
344static inline void checkStatus(Status status, const std::string& operation)
345{
346 if (status != Status::Success) {
347 std::cerr << operation << " failed" << std::endl;
348 }
349}
350
351} // namespace AQNWB
AQNWB::Types::Status Status
Definition BaseIO.hpp:21
AQNWB::Types::SizeType SizeType
Definition Channel.hpp:8
static constexpr SizeType SizeTypeNotSet
Value to use to indicate that a SizeType index is not set.
Definition Types.hpp:105
Definition Utils.hpp:21
std::tm to_utc_time(std::time_t time_value)
Convert a std::time_t value to a UTC std::tm structure.
Definition Utils.hpp:48
std::tm to_local_time(std::time_t time_value)
Convert a std::time_t value to a local std::tm structure.
Definition Utils.hpp:27
std::string format_utc_offset(long offset_seconds)
Format a UTC offset in seconds as a string (+HH:MM or -HH:MM).
Definition Utils.hpp:89
uint16_t to_little_endian_u16(uint16_t value)
Convert a 16-bit unsigned integer to little-endian byte order.
Definition Utils.hpp:107
long get_utc_offset_seconds(std::time_t time_value)
Get the UTC offset in seconds for a given time_t value.
Definition Utils.hpp:69
The main namespace for AqNWB.
Definition Channel.hpp:11
static Status intToStatus(int status)
Convert an integer status code to a Types::Status enum value. Shorthand for return (status < 0) ?...
Definition Utils.hpp:334
static std::unique_ptr< int16_t[]> transformToInt16(SizeType numSamples, float conversion_factor, const float *data)
Method to scale float values and convert to int16 values.
Definition Utils.hpp:299
static void convertFloatToInt16LE(const float *source, void *dest, SizeType numSamples)
Method to convert float values to uint16 values. This method was adapted from JUCE AudioDataConverter...
Definition Utils.hpp:272
static std::string mergePaths(const std::string &path1, const std::string &path2)
Merge two paths into a single path, handling extra trailing and starting "/".
Definition Utils.hpp:225
static bool isISO8601Date(const std::string &dateStr)
Check that a string is formatted in ISO8601 format.
Definition Utils.hpp:183
static std::string generateUuid()
Generates a UUID (Universally Unique Identifier) as a string.
Definition Utils.hpp:123
static void checkStatus(Status status, const std::string &operation)
Check status and print to standard error.
Definition Utils.hpp:344
static std::shared_ptr< IO::BaseIO > createIO(const std::string &type, const std::string &filename)
Factory method to create an IO object of the specified type.
Definition Utils.hpp:202
static std::string getCurrentTime()
Get the current time in ISO 8601 format with the UTC offset.
Definition Utils.hpp:157
static bool isValidIndex(SizeType index)
Check if a SizeType index is valid (i.e., not equal to SizeTypeNotSet).
Definition Utils.hpp:323