2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2015 - Scilab Enterprises - Antoine ELIAS
5 * This file must be used under the terms of the CeCILL.
6 * This source file is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at
9 * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
16 #include "hdf5_gw.hxx"
17 #include "context.hxx"
23 #include "polynom.hxx"
25 #include "macrofile.hxx"
26 #include "graphichandle.hxx"
28 #include "overload.hxx"
29 #include "execvisitor.hxx"
30 #include "handle_properties.hxx"
31 #include "context.hxx"
32 #include "serializervisitor.hxx"
36 #include "sci_malloc.h"
38 #include "localization.h"
39 #include "freeArrayOfString.h"
40 #include "os_string.h"
41 #include "deleteafile.h"
42 #include "expandPathVariable.h"
43 #include "h5_fileManagement.h"
44 #include "h5_writeDataToFile.h"
45 #include "h5_readDataFromFile.h"
46 #include "h5_attributeConstants.h"
47 #include "HandleManagement.h"
50 /*--------------------------------------------------------------------------*/
51 static bool isVarExist(int _iFile, const char* _pstVarName);
52 static int extractVarNameList(int* pvCtx, int _iStart, int _iEnd, char** _pstNameList);
54 int export_data(int parent, const std::string& name, types::InternalType* data);
55 static int export_double(int parent, const std::string& name, types::Double* data);
56 static int export_string(int parent, const std::string& name, types::String* data);
57 static int export_boolean(int parent, const std::string& name, types::Bool* data);
58 static int export_list(int parent, const std::string& name, types::List* data);
59 static int export_struct(int parent, const std::string& name, types::Struct* data, const char* type = g_SCILAB_CLASS_STRUCT);
60 template <class T> static int export_int(int parent, const std::string& name, int type, const char* prec, T* data);
61 static int export_poly(int parent, const std::string& name, types::Polynom* data);
62 static int export_sparse(int parent, const std::string& name, types::Sparse* data);
63 static int export_cell(int parent, const std::string& name, types::Cell* data);
64 static int export_macro(int parent, const std::string& name, types::Macro* data);
65 static int export_usertype(int parent, const std::string& name, types::UserType* data);
67 static int export_boolean_sparse(int parent, const std::string& name, types::SparseBool* data);
68 static int export_handles(int parent, const std::string& name, types::GraphicHandle* data);
69 static int export_void(int parent, const std::string& name);
70 static int export_undefined(int parent, const std::string& name);
72 /*--------------------------------------------------------------------------*/
73 static const std::string fname("save");
74 /*--------------------------------------------------------------------------*/
75 types::Function::ReturnValue sci_hdf5_save(types::typed_list &in, int _iRetCount, types::typed_list &out)
78 bool bAppendMode = false;
79 int rhs = static_cast<int>(in.size());
81 std::map<std::string, types::InternalType*> vars;
82 symbol::Context* ctx = symbol::Context::getInstance();
84 /* Check the number of input argument */
87 Scierror(999, _("%s: Wrong number of input argument(s): at least %d expected.\n"), fname.data(), 2);
88 return types::Function::Error;
91 if (in[0]->getId() != types::InternalType::IdScalarString)
93 Scierror(999, _("%s: Wrong type for input argument #%d: A String expected.\n"), fname.data(), 1);
94 return types::Function::Error;
97 wchar_t* wfilename = expandPathVariableW(in[0]->getAs<types::String>()->get()[0]);
98 char* cfilename = wide_string_to_UTF8(wfilename);
103 for (int i = 1; i < rhs; ++i)
105 if (in[i]->getId() != types::InternalType::IdScalarString)
107 Scierror(999, _("%s: Wrong type for input argument #%d: A String expected.\n"), fname.data(), 1);
108 return types::Function::Error;
111 wchar_t* wvar = in[i]->getAs<types::String>()->get()[0];
112 if (wcscmp(wvar, L"-append") == 0)
118 types::InternalType* pIT = ctx->get(symbol::Symbol(wvar));
121 Scierror(999, _("%s: Wrong value for input argument #%d: Defined variable expected.\n"), fname.data(), i + 1);
122 return types::Function::Error;
125 char* cvar = wide_string_to_UTF8(wvar);
126 std::string var(cvar);
133 //check append option
137 iH5File = openHDF5File(filename.data(), bAppendMode);
140 iH5File = createHDF5File(filename.data());
144 int iVersion = getSODFormatAttribute(iH5File);
145 if (iVersion != SOD_FILE_VERSION)
147 //to update version must be the same
148 closeHDF5File(iH5File);
149 Scierror(999, _("%s: Wrong SOD file format version. Expected: %d Found: %d\n"), fname.data(), SOD_FILE_VERSION, iVersion);
150 return types::Function::Error;
156 iH5File = createHDF5File(filename.data());
164 Scierror(999, _("%s: Wrong value for input argument #%d: \"%s\" is a directory"), fname.data(), 1, filename.data());
168 Scierror(999, _("%s: Cannot open file %s.\n"), fname.data() , filename.data());
171 return types::Function::Error;
175 for (const auto var : vars)
177 if (isVarExist(iH5File, var.first.data()))
181 if (deleteHDF5Var(iH5File, var.first.data()))
183 closeHDF5File(iH5File);
184 Scierror(999, _("%s: Unable to delete existing variable \"%s\".\n"), fname.data(), var.first.data());
185 return types::Function::Error;
190 closeHDF5File(iH5File);
191 Scierror(999, _("%s: Variable \'%s\' already exists in file \'%s\'\nUse -append option to replace existing variable.\n"), fname.data(), var.first.data(), filename.data());
192 return types::Function::Error;
196 int iDataset = export_data(iH5File, var.first, var.second);
199 closeHDF5File(iH5File);
200 deleteafile(filename.data());
201 Scierror(999, _("%s: Unable to export variable \'%s\' in file \'%s\'.\n"), fname.data(), var.first.data(), filename.data());
202 return types::Function::Error;
206 //add or update scilab version and file version in hdf5 file
207 if (updateScilabVersion(iH5File) < 0)
209 closeHDF5File(iH5File);
210 Scierror(999, _("%s: Unable to update Scilab version in \"%s\"."), fname.data(), filename.data());
211 return types::Function::Error;
214 if (updateFileVersion(iH5File) < 0)
216 closeHDF5File(iH5File);
217 Scierror(999, _("%s: Unable to update HDF5 format version in \"%s\"."), fname.data(), filename.data());
218 return types::Function::Error;
222 closeHDF5File(iH5File);
223 return types::Function::OK;
225 /*--------------------------------------------------------------------------*/
226 static bool isVarExist(int _iFile, const char* _pstVarName)
228 //check if variable already exists
229 int iNbItem = getVariableNames6(_iFile, NULL);
232 char **pstVarNameList = (char **)MALLOC(sizeof(char *) * iNbItem);
234 iNbItem = getVariableNames6(_iFile, pstVarNameList);
237 for (int i = 0; i < iNbItem; i++)
239 if (strcmp(pstVarNameList[i], _pstVarName) == 0)
241 freeArrayOfString(pstVarNameList, iNbItem);
246 freeArrayOfString(pstVarNameList, iNbItem);
251 /*--------------------------------------------------------------------------*/
252 int export_data(int parent, const std::string& name, types::InternalType* data)
255 switch (data->getType())
257 case types::InternalType::ScilabDouble:
258 dataset = export_double(parent, name, data->getAs<types::Double>());
260 case types::InternalType::ScilabString:
261 dataset = export_string(parent, name, data->getAs<types::String>());
263 case types::InternalType::ScilabBool:
264 dataset = export_boolean(parent, name, data->getAs<types::Bool>());
266 case types::InternalType::ScilabTList:
267 case types::InternalType::ScilabList:
268 case types::InternalType::ScilabMList:
269 dataset = export_list(parent, name, data->getAs<types::List>());
271 case types::InternalType::ScilabInt8:
272 dataset = export_int(parent, name, H5T_NATIVE_INT8, "8", data->getAs<types::Int8>());
274 case types::InternalType::ScilabInt16:
275 dataset = export_int(parent, name, H5T_NATIVE_INT16, "16", data->getAs<types::Int16>());
277 case types::InternalType::ScilabInt32:
278 dataset = export_int(parent, name, H5T_NATIVE_INT32, "32", data->getAs<types::Int32>());
280 case types::InternalType::ScilabInt64:
281 dataset = export_int(parent, name, H5T_NATIVE_INT64, "64", data->getAs<types::Int64>());
283 case types::InternalType::ScilabUInt8:
284 dataset = export_int(parent, name, H5T_NATIVE_UINT8, "u8", data->getAs<types::UInt8>());
286 case types::InternalType::ScilabUInt16:
287 dataset = export_int(parent, name, H5T_NATIVE_UINT16, "u16", data->getAs<types::UInt16>());
289 case types::InternalType::ScilabUInt32:
290 dataset = export_int(parent, name, H5T_NATIVE_UINT32, "u32", data->getAs<types::UInt32>());
292 case types::InternalType::ScilabUInt64:
293 dataset = export_int(parent, name, H5T_NATIVE_UINT64, "u64", data->getAs<types::UInt64>());
295 case types::InternalType::ScilabStruct:
296 dataset = export_struct(parent, name, data->getAs<types::Struct>());
298 case types::InternalType::ScilabPolynom:
299 dataset = export_poly(parent, name, data->getAs<types::Polynom>());
301 case types::InternalType::ScilabSparse:
302 dataset = export_sparse(parent, name, data->getAs<types::Sparse>());
304 case types::InternalType::ScilabSparseBool :
305 dataset = export_boolean_sparse(parent, name, data->getAs<types::SparseBool>());
307 case types::InternalType::ScilabCell:
308 dataset = export_cell(parent, name, data->getAs<types::Cell>());
310 case types::InternalType::ScilabVoid:
311 dataset = export_void(parent, name);
313 case types::InternalType::ScilabListUndefinedOperation:
314 dataset = export_undefined(parent, name);
316 case types::InternalType::ScilabMacro:
317 dataset = export_macro(parent, name, data->getAs<types::Macro>());
319 case types::InternalType::ScilabMacroFile:
321 types::MacroFile* pMF = data->getAs<types::MacroFile>();
322 dataset = export_macro(parent, name, pMF->getMacro());
325 case types::InternalType::ScilabHandle:
326 dataset = export_handles(parent, name, data->getAs<types::GraphicHandle>());
328 case types::InternalType::ScilabUserType:
329 dataset = export_usertype(parent, name, data->getAs<types::UserType>());
340 /*--------------------------------------------------------------------------*/
341 static int export_list(int parent, const std::string& name, types::List* data)
343 int size = data->getSize();
345 const char* type = nullptr;
346 switch (data->getType())
348 case types::InternalType::ScilabMList:
349 type = g_SCILAB_CLASS_MLIST;
351 case types::InternalType::ScilabTList:
352 type = g_SCILAB_CLASS_TLIST;
354 case types::InternalType::ScilabList:
355 type = g_SCILAB_CLASS_LIST;
361 //create node with list name
362 int dset = openList6(parent, name.data(), type);
364 for (int i = 0; i < size; ++i)
366 if (export_data(dset, std::to_string(i).data(), data->get(i)) == -1)
373 if (closeList6(dset) == -1)
379 /*--------------------------------------------------------------------------*/
380 static int export_double(int parent, const std::string& name, types::Double* data)
384 if (data->isComplex())
386 dataset = writeDoubleComplexMatrix6(parent, name.data(), data->getDims(), data->getDimsArray(), data->get(), data->getImg());
390 dataset = writeDoubleMatrix6(parent, name.data(), data->getDims(), data->getDimsArray(), data->get());
395 /*--------------------------------------------------------------------------*/
397 static int export_int(int parent, const std::string& name, int type, const char* prec, T* data)
399 return writeIntegerMatrix6(parent, name.data(), type, prec, data->getDims(), data->getDimsArray(), data->get());
401 /*--------------------------------------------------------------------------*/
402 static int export_string(int parent, const std::string& name, types::String* data)
404 int size = data->getSize();
405 wchar_t** s = data->get();
406 std::vector<char*> v(size);
408 //convert UTF16 strings to UTF8
409 for (int i = 0; i < size; ++i)
411 v[i] = wide_string_to_UTF8(s[i]);
414 int dset = writeStringMatrix6(parent, name.data(), data->getDims(), data->getDimsArray(), v.data());
417 for (int i = 0; i < size; ++i)
424 /*--------------------------------------------------------------------------*/
425 static int export_boolean(int parent, const std::string& name, types::Bool* data)
427 return writeBooleanMatrix6(parent, name.data(), data->getDims(), data->getDimsArray(), data->get());
429 /*--------------------------------------------------------------------------*/
430 static int export_struct(int parent, const std::string& name, types::Struct* data, const char* type)
432 //create a group with struct name
433 int dset = openList6(parent, name.data(), type);
434 //store struct dimensions
435 std::vector<int> dims = {1, data->getDims()};
436 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
442 int size = data->getSize();
446 if (closeList6(dset) == -1)
454 //create a node for fields references
455 int refs = openList6(dset, "__refs__", g_SCILAB_CLASS_STRUCT);
461 types::String* fields = data->getFieldNames();
462 int fieldCount = fields->getSize();
463 wchar_t** pfields = fields->get();
466 std::vector<hobj_ref_t> vrefs(size);
467 //fill main group with struct field name
468 for (int i = 0; i < fieldCount; ++i)
470 char* cfield = wide_string_to_UTF8(pfields[i]);
471 for (int j = 0; j < size; ++j)
474 types::InternalType* val = data->get(j)->get(pfields[i]);
476 std::string refname(cfield);
477 refname += "_" + std::to_string(j);
478 //export data in refs group
479 int ref = export_data(refs, refname, val);
480 //create reference of data
481 ret = addItemStruct6(refs, vrefs.data(), j, refname.data());
488 ret = writeStructField6(dset, cfield, data->getDims(), data->getDimsArray(), vrefs.data());
496 if (closeList6(refs) == -1)
501 if (closeList6(dset) == -1)
508 /*--------------------------------------------------------------------------*/
509 static int export_void(int parent, const std::string& name)
511 return writeVoid6(parent, name.data());
513 /*--------------------------------------------------------------------------*/
514 static int export_undefined(int parent, const std::string& name)
516 return writeUndefined6(parent, name.data());
518 /*--------------------------------------------------------------------------*/
519 static int export_poly(int parent, const std::string& name, types::Polynom* data)
521 //create a group with struct name
522 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_POLY);
523 //store struct dimensions
524 std::vector<int> dims = {1, data->getDims()};
525 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
531 //store variable name
532 std::vector<int> vardims = {1, 1};
533 char* varname = wide_string_to_UTF8(data->getVariableName().data());
534 ret = writeStringMatrix6(dset, "__varname__", 2, vardims.data(), &varname);
541 //create a node for fields references
542 int refs = openList6(dset, "__refs__", g_SCILAB_CLASS_POLY);
548 bool complex = data->isComplex();
549 int size = data->getSize();
550 std::vector<hobj_ref_t> vrefs(size);
551 types::SinglePoly** ss = data->get();
552 //fill main group with struct field name
553 for (int j = 0; j < size; ++j)
556 types::SinglePoly* val = ss[j];
557 //export data in refs group
558 std::vector<int> ssdims = {1, val->getSize()};
559 std::string polyname(std::to_string(j));
562 writeDoubleComplexMatrix6(refs, polyname.data(), 2, ssdims.data(), val->get(), val->getImg());
566 writeDoubleMatrix6(refs, polyname.data(), 2, ssdims.data(), val->get());
569 //create reference of data
570 ret = addItemStruct6(refs, vrefs.data(), j, polyname.data());
578 if (closeList6(refs) == -1)
583 if (closeList6(dset) == -1)
590 /*--------------------------------------------------------------------------*/
591 static int export_sparse(int parent, const std::string& name, types::Sparse* data)
593 int nnz = static_cast<int>(data->nonZeros());
594 int row = data->getRows();
595 //create a group with sparse name
596 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_SPARSE);
597 //store sparse dimensions
598 std::vector<int> dims = {1, data->getDims()};
599 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
605 //store numbers of non zero by rows.
606 std::vector<int> dimsnnz = {1, 1};
607 ret = writeIntegerMatrix6(dset, "__nnz__", H5T_NATIVE_INT32, "32", 2, dimsnnz.data(), &nnz);
615 int* inner = data->getInnerPtr(&innercount);
616 std::vector<int> dimsinner = {1, nnz};
617 ret = writeIntegerMatrix6(dset, "__inner__", H5T_NATIVE_INT32, "32", 2, dimsinner.data(), inner);
624 int* outer = data->getOuterPtr(&outercount);
625 std::vector<int> dimsouter = {1, outercount + 1};
626 ret = writeIntegerMatrix6(dset, "__outer__", H5T_NATIVE_INT32, "32", 2, dimsouter.data(), outer);
632 if (data->isComplex())
634 double* real = new double[nnz];
635 double* img = new double[nnz];
636 std::complex<double>* d = data->getImg();
637 for (int i = 0; i < nnz; ++i)
639 real[i] = d[i].real();
640 img[i] = d[i].imag();
644 std::vector<int> dimsdata = {1, nnz};
645 ret = writeDoubleComplexMatrix6(dset, "__data__", 2, dimsdata.data(), real, img);
655 std::vector<int> dimsdata = {1, nnz};
656 ret = writeDoubleMatrix6(dset, "__data__", 2, dimsdata.data(), data->get());
663 if (closeList6(dset) == -1)
671 /*--------------------------------------------------------------------------*/
672 static int export_boolean_sparse(int parent, const std::string& name, types::SparseBool* data)
674 int nnz = static_cast<int>(data->nbTrue());
675 int row = data->getRows();
676 //create a group with sparse name
677 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_BSPARSE);
678 //store sparse dimensions
679 std::vector<int> dims = {1, data->getDims()};
680 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
686 //store numbers of non zero by rows.
687 std::vector<int> dimsnnz = {1, 1};
688 ret = writeIntegerMatrix6(dset, "__nnz__", H5T_NATIVE_INT32, "32", 2, dimsnnz.data(), &nnz);
696 int* inner = data->getInnerPtr(&innercount);
697 std::vector<int> dimsinner = {1, nnz};
698 ret = writeIntegerMatrix6(dset, "__inner__", H5T_NATIVE_INT32, "32", 2, dimsinner.data(), inner);
705 int* outer = data->getOuterPtr(&outercount);
706 std::vector<int> dimsouter = {1, outercount + 1};
707 ret = writeIntegerMatrix6(dset, "__outer__", H5T_NATIVE_INT32, "32", 2, dimsouter.data(), outer);
713 if (closeList6(dset) == -1)
720 /*--------------------------------------------------------------------------*/
721 static int export_cell(int parent, const std::string& name, types::Cell* data)
723 //create a group with cell name
724 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_CELL);
725 //store cell dimensions
726 std::vector<int> dims = {1, data->getDims()};
727 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
733 //create a node for fields references
734 int refs = openList6(dset, "__refs__", g_SCILAB_CLASS_CELL);
740 int size = data->getSize();
741 types::InternalType** it = data->get();
742 std::vector<hobj_ref_t> vrefs(size);
743 for (int i = 0; i < size; ++i)
745 std::string refname(std::to_string(i));
746 int ref = export_data(refs, refname, it[i]);
750 if (closeList6(refs) == -1)
755 if (closeList6(dset) == -1)
763 static int export_handles(int parent, const std::string& name, types::GraphicHandle* data)
765 //create a group with cell name
766 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_HANDLE);
767 //store cell dimensions
768 std::vector<int> dims = {1, data->getDims()};
769 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
775 //create a node for fields references
776 int refs = openList6(dset, "__refs__", g_SCILAB_CLASS_HANDLE);
783 int size = data->getSize();
784 long long* ll = data->get();
785 std::vector<hobj_ref_t> vrefs(size);
786 for (int i = 0; i < size; ++i)
789 int hl = getObjectFromHandle(static_cast<long>(ll[i]));
790 std::string refname(std::to_string(i));
791 if (export_handle(refs, refname, hl) == false)
804 static int export_macro(int parent, const std::string& name, types::Macro* data)
808 //create a group with macro name
809 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_MACRO);
812 std::vector<char*> inputNames;
813 auto inputs = data->getInputs();
814 for (auto& input : *inputs)
816 inputNames.push_back(wide_string_to_UTF8(input->getSymbol().getName().data()));
820 dims[1] = static_cast<int>(inputNames.size());
821 writeStringMatrix6(dset, "inputs", 2, dims, inputNames.data());
823 for (auto& in : inputNames)
829 std::vector<char*> outputNames;
830 auto outputs = data->getOutputs();
831 for (auto& output : *outputs)
833 outputNames.push_back(wide_string_to_UTF8(output->getSymbol().getName().data()));
837 dims[1] = static_cast<int>(outputNames.size());
838 writeStringMatrix6(dset, "outputs", 2, dims, outputNames.data());
840 for (auto& in : outputNames)
846 ast::Exp* pExp = data->getBody();
847 ast::SerializeVisitor serialMacro(pExp);
849 unsigned char* serialAst = serialMacro.serialize();
850 //size if the buffer ( unsigned int )
851 unsigned int size = *((unsigned int*)serialAst);
855 writeIntegerMatrix6(dset, "body", H5T_NATIVE_UINT8, "u8", 2, dims, serialAst);
862 static int export_usertype(int parent, const std::string& name, types::UserType* data)
864 types::InternalType* it = data->save();
867 types::typed_list in;
870 types::typed_list out;
872 ast::ExecVisitor exec;
874 std::wstring wstFuncName = L"%" + data->getShortTypeStr() + L"_save";
878 types::Callable::ReturnValue ret = Overload::call(wstFuncName, in, 1, out, &exec);
880 if (ret != types::Callable::OK)
896 catch (ast::ScilabError& /*se*/)
898 //overload does not exist
904 if (it->isUserType())
910 //create a struct around "usertype" to be able to restore it.
911 types::Struct* str = new types::Struct(1, 1);
912 types::SingleStruct* ss = str->get()[0];
915 ss->addField(L"type");
916 ss->addField(L"data");
918 //assign values to new fields
919 ss->set(L"type", new types::String(data->getShortTypeStr().data()));
920 ss->set(L"data", it);
922 int ret = export_struct(parent, name, str, g_SCILAB_CLASS_USERTYPE);
924 //protect data against delete