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();
465 //save fields list in vector to keep order
466 export_string(dset, "__fields__", fields);
469 std::vector<hobj_ref_t> vrefs(size);
470 //fill main group with struct field name
471 for (int i = 0; i < fieldCount; ++i)
473 char* cfield = wide_string_to_UTF8(pfields[i]);
474 for (int j = 0; j < size; ++j)
477 types::InternalType* val = data->get(j)->get(pfields[i]);
479 std::string refname(cfield);
480 refname += "_" + std::to_string(j);
481 //export data in refs group
482 int ref = export_data(refs, refname, val);
483 //create reference of data
484 ret = addItemStruct6(refs, vrefs.data(), j, refname.data());
491 ret = writeStructField6(dset, cfield, data->getDims(), data->getDimsArray(), vrefs.data());
499 if (closeList6(refs) == -1)
504 if (closeList6(dset) == -1)
511 /*--------------------------------------------------------------------------*/
512 static int export_void(int parent, const std::string& name)
514 return writeVoid6(parent, name.data());
516 /*--------------------------------------------------------------------------*/
517 static int export_undefined(int parent, const std::string& name)
519 return writeUndefined6(parent, name.data());
521 /*--------------------------------------------------------------------------*/
522 static int export_poly(int parent, const std::string& name, types::Polynom* data)
524 //create a group with struct name
525 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_POLY);
526 //store struct dimensions
527 std::vector<int> dims = {1, data->getDims()};
528 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
534 //store variable name
535 std::vector<int> vardims = {1, 1};
536 char* varname = wide_string_to_UTF8(data->getVariableName().data());
537 ret = writeStringMatrix6(dset, "__varname__", 2, vardims.data(), &varname);
544 //create a node for fields references
545 int refs = openList6(dset, "__refs__", g_SCILAB_CLASS_POLY);
551 bool complex = data->isComplex();
552 int size = data->getSize();
553 std::vector<hobj_ref_t> vrefs(size);
554 types::SinglePoly** ss = data->get();
555 //fill main group with struct field name
556 for (int j = 0; j < size; ++j)
559 types::SinglePoly* val = ss[j];
560 //export data in refs group
561 std::vector<int> ssdims = {1, val->getSize()};
562 std::string polyname(std::to_string(j));
565 writeDoubleComplexMatrix6(refs, polyname.data(), 2, ssdims.data(), val->get(), val->getImg());
569 writeDoubleMatrix6(refs, polyname.data(), 2, ssdims.data(), val->get());
572 //create reference of data
573 ret = addItemStruct6(refs, vrefs.data(), j, polyname.data());
581 if (closeList6(refs) == -1)
586 if (closeList6(dset) == -1)
593 /*--------------------------------------------------------------------------*/
594 static int export_sparse(int parent, const std::string& name, types::Sparse* data)
596 int nnz = static_cast<int>(data->nonZeros());
597 int row = data->getRows();
598 //create a group with sparse name
599 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_SPARSE);
600 //store sparse dimensions
601 std::vector<int> dims = {1, data->getDims()};
602 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
608 //store numbers of non zero by rows.
609 std::vector<int> dimsnnz = {1, 1};
610 ret = writeIntegerMatrix6(dset, "__nnz__", H5T_NATIVE_INT32, "32", 2, dimsnnz.data(), &nnz);
618 int* inner = data->getInnerPtr(&innercount);
619 std::vector<int> dimsinner = {1, nnz};
620 ret = writeIntegerMatrix6(dset, "__inner__", H5T_NATIVE_INT32, "32", 2, dimsinner.data(), inner);
627 int* outer = data->getOuterPtr(&outercount);
628 std::vector<int> dimsouter = {1, outercount + 1};
629 ret = writeIntegerMatrix6(dset, "__outer__", H5T_NATIVE_INT32, "32", 2, dimsouter.data(), outer);
635 if (data->isComplex())
637 double* real = new double[nnz];
638 double* img = new double[nnz];
639 std::complex<double>* d = data->getImg();
640 for (int i = 0; i < nnz; ++i)
642 real[i] = d[i].real();
643 img[i] = d[i].imag();
647 std::vector<int> dimsdata = {1, nnz};
648 ret = writeDoubleComplexMatrix6(dset, "__data__", 2, dimsdata.data(), real, img);
658 std::vector<int> dimsdata = {1, nnz};
659 ret = writeDoubleMatrix6(dset, "__data__", 2, dimsdata.data(), data->get());
666 if (closeList6(dset) == -1)
674 /*--------------------------------------------------------------------------*/
675 static int export_boolean_sparse(int parent, const std::string& name, types::SparseBool* data)
677 int nnz = static_cast<int>(data->nbTrue());
678 int row = data->getRows();
679 //create a group with sparse name
680 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_BSPARSE);
681 //store sparse dimensions
682 std::vector<int> dims = {1, data->getDims()};
683 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
689 //store numbers of non zero by rows.
690 std::vector<int> dimsnnz = {1, 1};
691 ret = writeIntegerMatrix6(dset, "__nnz__", H5T_NATIVE_INT32, "32", 2, dimsnnz.data(), &nnz);
699 int* inner = data->getInnerPtr(&innercount);
700 std::vector<int> dimsinner = {1, nnz};
701 ret = writeIntegerMatrix6(dset, "__inner__", H5T_NATIVE_INT32, "32", 2, dimsinner.data(), inner);
708 int* outer = data->getOuterPtr(&outercount);
709 std::vector<int> dimsouter = {1, outercount + 1};
710 ret = writeIntegerMatrix6(dset, "__outer__", H5T_NATIVE_INT32, "32", 2, dimsouter.data(), outer);
716 if (closeList6(dset) == -1)
723 /*--------------------------------------------------------------------------*/
724 static int export_cell(int parent, const std::string& name, types::Cell* data)
726 //create a group with cell name
727 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_CELL);
728 //store cell dimensions
729 std::vector<int> dims = {1, data->getDims()};
730 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
736 //create a node for fields references
737 int refs = openList6(dset, "__refs__", g_SCILAB_CLASS_CELL);
743 int size = data->getSize();
744 types::InternalType** it = data->get();
745 std::vector<hobj_ref_t> vrefs(size);
746 for (int i = 0; i < size; ++i)
748 std::string refname(std::to_string(i));
749 int ref = export_data(refs, refname, it[i]);
753 if (closeList6(refs) == -1)
758 if (closeList6(dset) == -1)
766 static int export_handles(int parent, const std::string& name, types::GraphicHandle* data)
768 //create a group with cell name
769 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_HANDLE);
770 //store cell dimensions
771 std::vector<int> dims = {1, data->getDims()};
772 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
778 //create a node for fields references
779 int refs = openList6(dset, "__refs__", g_SCILAB_CLASS_HANDLE);
786 int size = data->getSize();
787 long long* ll = data->get();
788 std::vector<hobj_ref_t> vrefs(size);
789 for (int i = 0; i < size; ++i)
792 int hl = getObjectFromHandle(static_cast<long>(ll[i]));
793 std::string refname(std::to_string(i));
794 if (export_handle(refs, refname, hl) == false)
807 static int export_macro(int parent, const std::string& name, types::Macro* data)
811 //create a group with macro name
812 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_MACRO);
815 std::vector<char*> inputNames;
816 auto inputs = data->getInputs();
817 for (auto& input : *inputs)
819 inputNames.push_back(wide_string_to_UTF8(input->getSymbol().getName().data()));
823 dims[1] = static_cast<int>(inputNames.size());
824 writeStringMatrix6(dset, "inputs", 2, dims, inputNames.data());
826 for (auto& in : inputNames)
832 std::vector<char*> outputNames;
833 auto outputs = data->getOutputs();
834 for (auto& output : *outputs)
836 outputNames.push_back(wide_string_to_UTF8(output->getSymbol().getName().data()));
840 dims[1] = static_cast<int>(outputNames.size());
841 writeStringMatrix6(dset, "outputs", 2, dims, outputNames.data());
843 for (auto& in : outputNames)
849 ast::Exp* pExp = data->getBody();
850 ast::SerializeVisitor serialMacro(pExp);
852 unsigned char* serialAst = serialMacro.serialize();
853 //size if the buffer ( unsigned int )
854 unsigned int size = *((unsigned int*)serialAst);
858 writeIntegerMatrix6(dset, "body", H5T_NATIVE_UINT8, "u8", 2, dims, serialAst);
865 static int export_usertype(int parent, const std::string& name, types::UserType* data)
867 types::InternalType* it = data->save();
870 types::typed_list in;
873 types::typed_list out;
875 ast::ExecVisitor exec;
877 std::wstring wstFuncName = L"%" + data->getShortTypeStr() + L"_save";
881 types::Callable::ReturnValue ret = Overload::call(wstFuncName, in, 1, out, &exec);
883 if (ret != types::Callable::OK)
899 catch (ast::ScilabError& /*se*/)
901 //overload does not exist
907 if (it->isUserType())
913 //create a struct around "usertype" to be able to restore it.
914 types::Struct* str = new types::Struct(1, 1);
915 types::SingleStruct* ss = str->get()[0];
918 ss->addField(L"type");
919 ss->addField(L"data");
921 //assign values to new fields
922 ss->set(L"type", new types::String(data->getShortTypeStr().data()));
923 ss->set(L"data", it);
925 int ret = export_struct(parent, name, str, g_SCILAB_CLASS_USERTYPE);
927 //protect data against delete