SDDS ToolKit Programs and Libraries for C and Python
Loading...
Searching...
No Matches
test_sdds3_differential.cc
Go to the documentation of this file.
1/**
2 * @file test_sdds3_differential.cc
3 * @brief Differential serial-format tests between the C and C++ SDDS libraries.
4 *
5 * @details Writes representative files with each implementation and verifies
6 * that the other implementation reads the same layout and values.
7 *
8 * @copyright
9 * - (c) 2026 The University of Chicago
10 *
11 * @license
12 * This file is distributed under the terms of the Software License Agreement
13 * found in the file LICENSE included with this distribution.
14 */
15
16#include "SDDS.hpp"
17#include "SDDS.h"
18
19#include <cassert>
20#include <cmath>
21#include <cstdint>
22#include <cstdlib>
23#include <filesystem>
24#include <fstream>
25#include <string>
26#include <vector>
27
28namespace {
29
30std::vector<char> mutablePath(const std::filesystem::path &path);
31
32sdds::Layout allTypesLayout(sdds::DataMode mode = sdds::DataMode::Binary,
33 sdds::MajorOrder major = sdds::MajorOrder::Row,
34 sdds::ByteOrder order = sdds::ByteOrder::Native) {
35 sdds::DataOptions data;
36 data.mode = mode;
37 data.majorOrder = major;
38 data.byteOrder = order;
39 sdds::LayoutBuilder builder;
40 builder.setDataOptions(data)
41 .addParameter({{"p64", {}, {}, {}, {}, sdds::Type::Int64}, {}})
42 .addArray({{"array", {}, {}, {}, {}, sdds::Type::Double}, 0, 1, {}})
43 .addAssociate({"related", std::string("related.sdds"), std::string("."),
44 std::string("related data"), std::string("SDDS"), true})
45 .addColumn({{"longdouble", {}, {}, {}, {}, sdds::Type::LongDouble}, 0})
46 .addColumn({{"double", {}, {}, {}, {}, sdds::Type::Double}, 0})
47 .addColumn({{"float", {}, {}, {}, {}, sdds::Type::Float}, 0})
48 .addColumn({{"long64", {}, {}, {}, {}, sdds::Type::Int64}, 0})
49 .addColumn({{"ulong64", {}, {}, {}, {}, sdds::Type::UInt64}, 0})
50 .addColumn({{"long", {}, {}, {}, {}, sdds::Type::Int32}, 0})
51 .addColumn({{"ulong", {}, {}, {}, {}, sdds::Type::UInt32}, 0})
52 .addColumn({{"short", {}, {}, {}, {}, sdds::Type::Int16}, 0})
53 .addColumn({{"ushort", {}, {}, {}, {}, sdds::Type::UInt16}, 0})
54 .addColumn({{"string", {}, {}, {}, {}, sdds::Type::String}, 0})
55 .addColumn({{"character", {}, {}, {}, {}, sdds::Type::Character}, 0});
56 return builder.build();
57}
58
59sdds::Page allTypesPage(const std::shared_ptr<const sdds::Layout> &layout) {
60 sdds::Page page(layout);
61 page.setParameter("p64", std::int64_t{1234567890123});
62 page.setArray("array", {{3}, std::vector<double>{1.25, 2.5, 5.0}});
63 page.setColumn("longdouble", std::vector<long double>{1.25L, -2.5L});
64 page.setColumn("double", std::vector<double>{3.25, -4.5});
65 page.setColumn("float", std::vector<float>{5.25F, -6.5F});
66 page.setColumn("long64", std::vector<std::int64_t>{-7, INT64_C(5000000000)});
67 page.setColumn("ulong64", std::vector<std::uint64_t>{8, UINT64_C(9000000000)});
68 page.setColumn("long", std::vector<std::int32_t>{-9, 10});
69 page.setColumn("ulong", std::vector<std::uint32_t>{11, 12});
70 page.setColumn("short", std::vector<std::int16_t>{-13, 14});
71 page.setColumn("ushort", std::vector<std::uint16_t>{15, 16});
72 page.setColumn("string", std::vector<std::string>{"alpha", "space value"});
73 page.setColumn("character", std::vector<char>{'A', '!'});
74 return page;
75}
76
77void cWrite(const std::filesystem::path &path) {
78 SDDS_DATASET dataset{};
79 auto filename = mutablePath(path);
80 assert(SDDS_InitializeOutput(&dataset, SDDS_BINARY, 1, nullptr,
81 const_cast<char *>("C differential output"),
82 filename.data()));
83 assert(SDDS_DefineSimpleParameter(&dataset, "p64", nullptr, SDDS_LONG64));
84 assert(SDDS_DefineArray(&dataset, const_cast<char *>("array"), nullptr, nullptr, nullptr,
85 nullptr, SDDS_DOUBLE, 0, 1, nullptr) >= 0);
86 const char *names[] = {"longdouble", "double", "float", "long64", "ulong64", "long",
87 "ulong", "short", "ushort", "string", "character"};
88 const std::int32_t types[] = {SDDS_LONGDOUBLE, SDDS_DOUBLE, SDDS_FLOAT, SDDS_LONG64,
91 for (std::size_t index = 0; index < sizeof(types) / sizeof(types[0]); ++index)
92 assert(SDDS_DefineSimpleColumn(&dataset, names[index], nullptr, types[index]));
93 assert(SDDS_WriteLayout(&dataset));
94 assert(SDDS_StartPage(&dataset, 2));
95 assert(SDDS_SetParameters(&dataset, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE,
96 "p64", INT64_C(1234567890123), nullptr));
97 double array[] = {1.25, 2.5, 5.0};
98 std::int32_t dimensions[] = {3};
99 assert(SDDS_SetArray(&dataset, const_cast<char *>("array"), SDDS_CONTIGUOUS_DATA,
100 array, dimensions));
101 long double longdoubleValues[] = {1.25L, -2.5L};
102 double doubleValues[] = {3.25, -4.5};
103 float floatValues[] = {5.25F, -6.5F};
104 std::int64_t long64Values[] = {-7, INT64_C(5000000000)};
105 std::uint64_t ulong64Values[] = {8, UINT64_C(9000000000)};
106 std::int32_t longValues[] = {-9, 10};
107 std::uint32_t ulongValues[] = {11, 12};
108 std::int16_t shortValues[] = {-13, 14};
109 std::uint16_t ushortValues[] = {15, 16};
110 char alpha[] = "alpha";
111 char spaced[] = "space value";
112 char *stringValues[] = {alpha, spaced};
113 char characterValues[] = {'A', '!'};
114 void *values[] = {longdoubleValues, doubleValues, floatValues, long64Values, ulong64Values,
115 longValues, ulongValues, shortValues, ushortValues, stringValues,
116 characterValues};
117 for (std::size_t index = 0; index < sizeof(values) / sizeof(values[0]); ++index)
118 assert(SDDS_SetColumn(&dataset, SDDS_SET_BY_NAME, values[index], 2, names[index]));
119 assert(SDDS_WritePage(&dataset));
120 assert(SDDS_Terminate(&dataset));
121}
122
123void cppRead(const std::filesystem::path &path) {
124 sdds::ReaderOptions options;
125#if defined(_WIN32)
126 options.longDoubleEncoding = sdds::LongDoubleEncoding::LegacyFloat64;
127#endif
128 auto reader = sdds::Reader::open(path, options);
129 auto page = reader.next();
130 assert(page && page->rowCount() == 2);
131 assert(page->parameterAs<std::int64_t>("p64") == INT64_C(1234567890123));
132 assert(page->arrayAs<double>("array")[2] == 5.0);
133 assert(std::fabs(page->columnAs<long double>("longdouble")[1] + 2.5L) < 1e-12L);
134 assert(page->columnAs<std::uint64_t>("ulong64")[1] == UINT64_C(9000000000));
135 assert(page->columnAs<std::string>("string")[1] == "space value");
136 assert(page->columnAs<char>("character")[1] == '!');
137 reader.close();
138}
139
140void cppWrite(const std::filesystem::path &path, sdds::DataMode mode,
141 sdds::MajorOrder major, sdds::ByteOrder order,
142 sdds::Compression compression = sdds::Compression::None) {
143 const auto layout = allTypesLayout(mode, major, order);
144 sdds::WriterOptions options;
145 options.compression = compression;
146#if defined(_WIN32)
147 options.longDoubleEncoding = sdds::LongDoubleEncoding::LegacyFloat64;
148#endif
149 auto writer = sdds::Writer::create(path, layout, options);
150 writer.write(allTypesPage(std::make_shared<const sdds::Layout>(layout)));
151 writer.close();
152 auto reader = sdds::Reader::open(path);
153 assert(reader.layout().associates.size() == 1);
154 assert(reader.layout().associates[0].name == "related");
155 assert(reader.layout().associates[0].isSdds);
156 reader.close();
157}
158
159std::vector<char> mutablePath(const std::filesystem::path &path) {
160 const std::string text = path.string();
161 std::vector<char> result(text.begin(), text.end());
162 result.push_back('\0');
163 return result;
164}
165
166void cRead(const std::filesystem::path &path) {
167 SDDS_DATASET dataset{};
168 auto filename = mutablePath(path);
169 assert(SDDS_InitializeInput(&dataset, filename.data()));
170 assert(SDDS_ReadPage(&dataset) == 1);
171 assert(SDDS_CountRowsOfInterest(&dataset) == 2);
172 std::int64_t parameter = 0;
173 assert(SDDS_GetParameter(&dataset, const_cast<char *>("p64"), &parameter));
174 assert(parameter == INT64_C(1234567890123));
175 auto *long64Values = static_cast<std::int64_t *>(
176 SDDS_GetColumn(&dataset, const_cast<char *>("long64")));
177 assert(long64Values && long64Values[1] == INT64_C(5000000000));
178 SDDS_Free(long64Values);
179 char **strings = SDDS_GetColumnInString(&dataset, const_cast<char *>("string"));
180 assert(strings && std::string(strings[1]) == "space value");
181 SDDS_FreeStringArray(strings, 2);
182 SDDS_Free(strings);
183 SDDS_ARRAY *array = SDDS_GetArray(&dataset, const_cast<char *>("array"), nullptr);
184 assert(array && array->elements == 3 && static_cast<double *>(array->data)[2] == 5.0);
185 SDDS_FreeArray(array);
186 assert(SDDS_Terminate(&dataset));
187}
188
189void cWriteRowMode(const std::filesystem::path &path, sdds::DataMode dataMode,
190 sdds::RowCountMode rowCountMode) {
191 SDDS_DATASET dataset{};
192 auto filename = mutablePath(path);
193 assert(SDDS_InitializeOutput(&dataset,
194 dataMode == sdds::DataMode::Ascii ? SDDS_ASCII : SDDS_BINARY,
195 1, nullptr, nullptr, filename.data()));
196 assert(SDDS_DefineSimpleColumn(&dataset, "x", nullptr, SDDS_DOUBLE));
197 const std::uint32_t cMode = rowCountMode == sdds::RowCountMode::Fixed ?
198 SDDS_FIXEDROWCOUNT : SDDS_NOROWCOUNT;
199 assert(SDDS_SetRowCountMode(&dataset, cMode));
200 assert(SDDS_WriteLayout(&dataset));
201 assert(SDDS_StartPage(&dataset, 2));
202 double values[] = {1.0, 2.0};
203 assert(SDDS_SetColumn(&dataset, SDDS_SET_BY_NAME, values, 2, "x"));
204 assert(SDDS_WritePage(&dataset));
205 assert(SDDS_Terminate(&dataset));
206}
207
208void cppReadRows(const std::filesystem::path &path, sdds::RowCountMode mode) {
209 auto reader = sdds::Reader::open(path);
210 assert(reader.layout().data.rowCountMode == mode);
211 auto page = reader.next();
212 assert(page && page->rowCount() == 2);
213 assert(page->columnAs<double>("x")[1] == 2.0);
214 reader.close();
215}
216
217void cppWriteRowMode(const std::filesystem::path &path, sdds::DataMode dataMode,
218 sdds::RowCountMode rowCountMode) {
219 sdds::DataOptions data;
220 data.mode = dataMode;
221 data.rowCountMode = rowCountMode;
222 sdds::LayoutBuilder builder;
223 builder.setDataOptions(data).addColumn({{"x", {}, {}, {}, {}, sdds::Type::Double}, 0});
224 const auto layout = builder.build();
225 auto writer = sdds::Writer::create(path, layout);
226 sdds::Page page(std::make_shared<const sdds::Layout>(layout));
227 page.setColumn("x", std::vector<double>{1.0, 2.0});
228 writer.write(std::move(page));
229 writer.close();
230
231 SDDS_DATASET dataset{};
232 auto filename = mutablePath(path);
233 assert(SDDS_InitializeInput(&dataset, filename.data()));
234 assert(SDDS_ReadPage(&dataset) == 1);
235 assert(SDDS_CountRowsOfInterest(&dataset) == 2);
236 assert(SDDS_Terminate(&dataset));
237}
238
239void rowCountMatrix(const std::filesystem::path &directory) {
240 for (const auto dataMode : {sdds::DataMode::Ascii, sdds::DataMode::Binary}) {
241 const std::string modeName = dataMode == sdds::DataMode::Ascii ? "ascii" : "binary";
242 const auto cPath = directory / ("c-fixed-" + modeName + ".sdds");
243 cWriteRowMode(cPath, dataMode, sdds::RowCountMode::Fixed);
244 cppReadRows(cPath, sdds::RowCountMode::Fixed);
245 const auto cppPath = directory / ("cpp-fixed-" + modeName + ".sdds");
246 cppWriteRowMode(cppPath, dataMode, sdds::RowCountMode::Fixed);
247 }
248 const auto cNoCount = directory / "c-no-row-count.sdds";
249 cWriteRowMode(cNoCount, sdds::DataMode::Ascii, sdds::RowCountMode::None);
250 cppReadRows(cNoCount, sdds::RowCountMode::None);
251 const auto cppNoCount = directory / "cpp-no-row-count.sdds";
252 cppWriteRowMode(cppNoCount, sdds::DataMode::Ascii, sdds::RowCountMode::None);
253}
254
255void versionMatrix(const std::filesystem::path &directory) {
256 sdds::LayoutBuilder builder;
257 builder.addColumn({{"x", {}, {}, {}, {}, sdds::Type::Double}, 0});
258 const auto layout = builder.build();
259 for (std::int32_t version = 1; version <= 5; ++version) {
260 const auto path = directory / ("version-" + std::to_string(version) + ".sdds");
261 sdds::WriterOptions options;
262 options.minimumVersion = version;
263 auto writer = sdds::Writer::create(path, layout, options);
264 sdds::Page page(std::make_shared<const sdds::Layout>(layout));
265 page.setColumn("x", std::vector<double>{1.0, 2.0});
266 writer.write(std::move(page));
267 writer.close();
268 SDDS_DATASET dataset{};
269 auto filename = mutablePath(path);
270 assert(SDDS_InitializeInput(&dataset, filename.data()));
271 assert(dataset.layout.version == version);
272 assert(SDDS_ReadPage(&dataset) == 1);
273 assert(SDDS_Terminate(&dataset));
274 }
275}
276
277void includeMatrix(const std::filesystem::path &directory) {
278 const auto includePath = std::filesystem::absolute(directory / "included-layout.sdds");
279 const auto rootPath = directory / "include-root.sdds";
280 {
281 std::ofstream included(includePath);
282 included << "&column name=included, type=long &end\n";
283 assert(included.good());
284 }
285 {
286 std::ofstream root(rootPath);
287 root << "SDDS1\n"
288 << "&include filename=\"" << includePath.generic_string() << "\" &end\n"
289 << "&data mode=ascii &end\n"
290 << "0\n";
291 assert(root.good());
292 }
293
294 auto reader = sdds::Reader::open(rootPath);
295 assert(reader.layout().columns.size() == 1);
296 assert(reader.layout().columns[0].name == "included");
297 auto page = reader.next();
298 assert(page && page->rowCount() == 0);
299 reader.close();
300
301 SDDS_DATASET dataset{};
302 auto filename = mutablePath(rootPath);
303 assert(SDDS_InitializeInput(&dataset, filename.data()));
304 assert(dataset.layout.n_columns == 1);
305 assert(std::string(dataset.layout.column_definition[0].name) == "included");
306 assert(SDDS_ReadPage(&dataset) == 1);
307 assert(SDDS_Terminate(&dataset));
308}
309
310} // namespace
311
312int main(int argc, char **argv) {
313#if defined(_WIN32)
314 _putenv_s("SDDS_LONGDOUBLE_64BITS", "1");
315#endif
316 if (argc != 2) return 2;
317 const std::filesystem::path directory(argv[1]);
318 std::filesystem::create_directories(directory);
319 const auto cOutput = directory / "c-to-cpp.sdds";
320 cWrite(cOutput);
321 cppRead(cOutput);
322 for (const auto mode : {sdds::DataMode::Ascii, sdds::DataMode::Binary}) {
323 for (const auto major : {sdds::MajorOrder::Row, sdds::MajorOrder::Column}) {
324 if (mode == sdds::DataMode::Ascii && major == sdds::MajorOrder::Column) continue;
325 for (const auto order : {sdds::ByteOrder::Little, sdds::ByteOrder::Big}) {
326 const auto path = directory / (std::string(mode == sdds::DataMode::Ascii ? "ascii" : "binary") +
327 (major == sdds::MajorOrder::Row ? "-row" : "-column") +
328 (order == sdds::ByteOrder::Little ? "-little.sdds" :
329 "-big.sdds"));
330 cppWrite(path, mode, major, order);
331 cRead(path);
332 }
333 }
334 }
335 for (const auto compression : {sdds::Compression::Gzip, sdds::Compression::Xz,
336 sdds::Compression::Lzma}) {
337 const char *extension = compression == sdds::Compression::Gzip ? ".sdds.gz" :
338 compression == sdds::Compression::Xz ? ".sdds.xz" : ".sdds.lzma";
339 const auto path = directory / (std::string("compressed") + extension);
340 cppWrite(path, sdds::DataMode::Binary, sdds::MajorOrder::Row,
341 sdds::ByteOrder::Little, compression);
342 cRead(path);
343 }
344 versionMatrix(directory);
345 rowCountMatrix(directory);
346 includeMatrix(directory);
347 return 0;
348}
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_StartPage(SDDS_DATASET *SDDS_dataset, int64_t expected_n_rows)
int32_t SDDS_SetParameters(SDDS_DATASET *SDDS_dataset, int32_t mode,...)
int32_t SDDS_SetColumn(SDDS_DATASET *SDDS_dataset, int32_t mode, void *data, int64_t rows,...)
Sets the values for one data column in the current data table of an SDDS dataset.
int32_t SDDS_SetArray(SDDS_DATASET *SDDS_dataset, char *array_name, int32_t mode, void *data_pointer, int32_t *dimension)
Sets the values of an array variable in the SDDS dataset using specified dimensions.
void * SDDS_GetColumn(SDDS_DATASET *SDDS_dataset, char *column_name)
Retrieves a copy of the data for a specified column, including only rows marked as "of interest".
int64_t SDDS_CountRowsOfInterest(SDDS_DATASET *SDDS_dataset)
Counts the number of rows marked as "of interest" in the current data table.
SDDS_ARRAY * SDDS_GetArray(SDDS_DATASET *SDDS_dataset, char *array_name, SDDS_ARRAY *memory)
Retrieves an array from the current data table of an SDDS dataset.
char ** SDDS_GetColumnInString(SDDS_DATASET *SDDS_dataset, char *column_name)
Retrieves the data of a specified column as an array of strings, considering only rows marked as "of ...
void * SDDS_GetParameter(SDDS_DATASET *SDDS_dataset, char *parameter_name, void *memory)
Retrieves the value of a specified parameter from the current data table of a data set.
int32_t SDDS_InitializeInput(SDDS_DATASET *SDDS_dataset, char *filename)
Definition SDDS_input.c:50
int32_t SDDS_Terminate(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_ReadPage(SDDS_DATASET *SDDS_dataset)
int32_t SDDS_InitializeOutput(SDDS_DATASET *SDDS_dataset, int32_t data_mode, int32_t lines_per_row, const char *description, const char *contents, const char *filename)
Initializes the SDDS output dataset.
int32_t SDDS_DefineSimpleColumn(SDDS_DATASET *SDDS_dataset, const char *name, const char *unit, int32_t type)
Defines a simple data column within the SDDS dataset.
int32_t SDDS_DefineSimpleParameter(SDDS_DATASET *SDDS_dataset, const char *name, const char *unit, int32_t type)
Defines a simple data parameter within the SDDS dataset.
int32_t SDDS_SetRowCountMode(SDDS_DATASET *SDDS_dataset, uint32_t mode)
Sets the row count mode for the SDDS dataset.
int32_t SDDS_DefineArray(SDDS_DATASET *SDDS_dataset, const char *name, const char *symbol, const char *units, const char *description, const char *format_string, int32_t type, int32_t field_length, int32_t dimensions, const char *group_name)
Defines a data array within the SDDS dataset.
int32_t SDDS_WritePage(SDDS_DATASET *SDDS_dataset)
Writes the current data table to the output file.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
void SDDS_FreeArray(SDDS_ARRAY *array)
Frees memory allocated for an SDDS array structure.
int32_t SDDS_FreeStringArray(char **string, int64_t strings)
Frees an array of strings by deallocating each individual string.
void SDDS_Free(void *mem)
Free memory previously allocated by SDDS_Malloc.
Definition SDDS_utils.c:721
#define SDDS_ULONG
Identifier for the unsigned 32-bit integer data type.
Definition SDDStypes.h:67
#define SDDS_FLOAT
Identifier for the float data type.
Definition SDDStypes.h:43
#define SDDS_STRING
Identifier for the string data type.
Definition SDDStypes.h:85
#define SDDS_ULONG64
Identifier for the unsigned 64-bit integer data type.
Definition SDDStypes.h:55
#define SDDS_LONG
Identifier for the signed 32-bit integer data type.
Definition SDDStypes.h:61
#define SDDS_SHORT
Identifier for the signed short integer data type.
Definition SDDStypes.h:73
#define SDDS_CHARACTER
Identifier for the character data type.
Definition SDDStypes.h:91
#define SDDS_USHORT
Identifier for the unsigned short integer data type.
Definition SDDStypes.h:79
#define SDDS_DOUBLE
Identifier for the double data type.
Definition SDDStypes.h:37
#define SDDS_LONGDOUBLE
Identifier for the long double data type.
Definition SDDStypes.h:31
#define SDDS_LONG64
Identifier for the signed 64-bit integer data type.
Definition SDDStypes.h:49