2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2015 - Scilab Enterprises - Antoine ELIAS
5 * Copyright (C) 2012 - 2016 - Scilab Enterprises
7 * This file is hereby licensed under the terms of the GNU GPL v2.0,
8 * pursuant to article 5.3.4 of the CeCILL v.2.1.
9 * This file was originally licensed under the terms of the CeCILL v2.1,
10 * and continues to be available under such terms.
11 * For more information, see the COPYING file which you should have received
12 * along with this program.
19 #include "hdf5_gw.hxx"
20 #include "context.hxx"
26 #include "polynom.hxx"
28 #include "macrofile.hxx"
29 #include "graphichandle.hxx"
31 #include "overload.hxx"
32 #include "handle_properties.hxx"
33 #include "context.hxx"
34 #include "serializervisitor.hxx"
38 #include "sci_malloc.h"
40 #include "localization.h"
41 #include "freeArrayOfString.h"
42 #include "os_string.h"
43 #include "deleteafile.h"
44 #include "expandPathVariable.h"
45 #include "h5_fileManagement.h"
46 #include "h5_writeDataToFile.h"
47 #include "h5_readDataFromFile.h"
48 #include "h5_attributeConstants.h"
49 #include "HandleManagement.h"
52 /*--------------------------------------------------------------------------*/
53 static bool isVarExist(int _iFile, const char* _pstVarName);
54 static int extractVarNameList(int* pvCtx, int _iStart, int _iEnd, char** _pstNameList);
56 int export_data(int parent, const std::string& name, types::InternalType* data);
57 static int export_double(int parent, const std::string& name, types::Double* data);
58 static int export_string(int parent, const std::string& name, types::String* data);
59 static int export_boolean(int parent, const std::string& name, types::Bool* data);
60 static int export_list(int parent, const std::string& name, types::List* data);
61 static int export_struct(int parent, const std::string& name, types::Struct* data, const char* type = g_SCILAB_CLASS_STRUCT);
62 template <class T> static int export_int(int parent, const std::string& name, int type, const char* prec, T* data);
63 static int export_poly(int parent, const std::string& name, types::Polynom* data);
64 static int export_sparse(int parent, const std::string& name, types::Sparse* data);
65 static int export_cell(int parent, const std::string& name, types::Cell* data);
66 static int export_macro(int parent, const std::string& name, types::Macro* data);
67 static int export_usertype(int parent, const std::string& name, types::UserType* data);
69 static int export_boolean_sparse(int parent, const std::string& name, types::SparseBool* data);
70 static int export_handles(int parent, const std::string& name, types::GraphicHandle* data);
71 static int export_void(int parent, const std::string& name);
72 static int export_undefined(int parent, const std::string& name);
74 /*--------------------------------------------------------------------------*/
75 static const std::string fname("save");
76 /*--------------------------------------------------------------------------*/
77 types::Function::ReturnValue sci_hdf5_save(types::typed_list &in, int _iRetCount, types::typed_list &out)
80 bool bAppendMode = false;
81 int rhs = static_cast<int>(in.size());
83 std::map<std::string, types::InternalType*> vars;
84 symbol::Context* ctx = symbol::Context::getInstance();
86 /* Check the number of input argument */
89 Scierror(999, _("%s: Wrong number of input argument(s): at least %d expected.\n"), fname.data(), 2);
90 return types::Function::Error;
93 if (in[0]->getId() != types::InternalType::IdScalarString)
95 Scierror(999, _("%s: Wrong type for input argument #%d: A String expected.\n"), fname.data(), 1);
96 return types::Function::Error;
99 wchar_t* wfilename = expandPathVariableW(in[0]->getAs<types::String>()->get()[0]);
100 char* cfilename = wide_string_to_UTF8(wfilename);
101 filename = cfilename;
108 //get variables in scope 1
109 std::list<std::wstring> lst;
110 int size = ctx->getConsoleVarsName(lst);
114 return types::Function::OK;
117 for (const auto & wvar : lst)
119 types::InternalType* pIT = ctx->getAtLevel(symbol::Symbol(wvar), SCOPE_CONSOLE);
121 //do not save macrofile
122 if (pIT->isMacroFile() || pIT->isFunction() || pIT->isLibrary())
127 char* cvar = wide_string_to_UTF8(wvar.data());
128 std::string var(cvar);
137 for (int i = 1; i < rhs; ++i)
139 if (in[i]->getId() != types::InternalType::IdScalarString)
141 Scierror(999, _("%s: Wrong type for input argument #%d: A String expected.\n"), fname.data(), i+1);
142 return types::Function::Error;
145 wchar_t* wvar = in[i]->getAs<types::String>()->get()[0];
146 if (wcscmp(wvar, L"-append") == 0)
152 types::InternalType* pIT = ctx->get(symbol::Symbol(wvar));
155 Scierror(999, _("%s: Wrong value for input argument #%d: Defined variable expected.\n"), fname.data(), i + 1);
156 return types::Function::Error;
159 char* cvar = wide_string_to_UTF8(wvar);
160 std::string var(cvar);
167 //check append option
171 iH5File = openHDF5File(filename.data(), bAppendMode);
174 iH5File = createHDF5File(filename.data());
178 int iVersion = getSODFormatAttribute(iH5File);
179 if (iVersion != SOD_FILE_VERSION)
181 //to update version must be the same
182 closeHDF5File(iH5File);
183 Scierror(999, _("%s: Wrong SOD file format version. Expected: %d Found: %d\n"), fname.data(), SOD_FILE_VERSION, iVersion);
184 return types::Function::Error;
190 iH5File = createHDF5File(filename.data());
198 Scierror(999, _("%s: Wrong value for input argument #%d: \"%s\" is a directory"), fname.data(), 1, filename.data());
202 Scierror(999, _("%s: Cannot open file %s.\n"), fname.data() , filename.data());
205 return types::Function::Error;
209 for (const auto var : vars)
211 if (isVarExist(iH5File, var.first.data()))
215 if (deleteHDF5Var(iH5File, var.first.data()))
217 closeHDF5File(iH5File);
218 Scierror(999, _("%s: Unable to delete existing variable \"%s\".\n"), fname.data(), var.first.data());
219 return types::Function::Error;
224 closeHDF5File(iH5File);
225 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());
226 return types::Function::Error;
230 int iDataset = export_data(iH5File, var.first, var.second);
233 closeHDF5File(iH5File);
234 deleteafile(filename.data());
235 Scierror(999, _("%s: Unable to export variable \'%s\' in file \'%s\'.\n"), fname.data(), var.first.data(), filename.data());
236 return types::Function::Error;
240 //add or update scilab version and file version in hdf5 file
241 if (updateScilabVersion(iH5File) < 0)
243 closeHDF5File(iH5File);
244 Scierror(999, _("%s: Unable to update Scilab version in \"%s\"."), fname.data(), filename.data());
245 return types::Function::Error;
248 if (updateFileVersion(iH5File) < 0)
250 closeHDF5File(iH5File);
251 Scierror(999, _("%s: Unable to update HDF5 format version in \"%s\"."), fname.data(), filename.data());
252 return types::Function::Error;
256 closeHDF5File(iH5File);
257 return types::Function::OK;
259 /*--------------------------------------------------------------------------*/
260 static bool isVarExist(int _iFile, const char* _pstVarName)
262 //check if variable already exists
263 int iNbItem = getVariableNames6(_iFile, NULL);
266 char **pstVarNameList = (char **)MALLOC(sizeof(char *) * iNbItem);
268 iNbItem = getVariableNames6(_iFile, pstVarNameList);
271 for (int i = 0; i < iNbItem; i++)
273 if (strcmp(pstVarNameList[i], _pstVarName) == 0)
275 freeArrayOfString(pstVarNameList, iNbItem);
280 freeArrayOfString(pstVarNameList, iNbItem);
285 /*--------------------------------------------------------------------------*/
286 int export_data(int parent, const std::string& name, types::InternalType* data)
289 switch (data->getType())
291 case types::InternalType::ScilabDouble:
292 dataset = export_double(parent, name, data->getAs<types::Double>());
294 case types::InternalType::ScilabString:
295 dataset = export_string(parent, name, data->getAs<types::String>());
297 case types::InternalType::ScilabBool:
298 dataset = export_boolean(parent, name, data->getAs<types::Bool>());
300 case types::InternalType::ScilabTList:
301 case types::InternalType::ScilabList:
302 case types::InternalType::ScilabMList:
303 dataset = export_list(parent, name, data->getAs<types::List>());
305 case types::InternalType::ScilabInt8:
306 dataset = export_int(parent, name, H5T_NATIVE_INT8, "8", data->getAs<types::Int8>());
308 case types::InternalType::ScilabInt16:
309 dataset = export_int(parent, name, H5T_NATIVE_INT16, "16", data->getAs<types::Int16>());
311 case types::InternalType::ScilabInt32:
312 dataset = export_int(parent, name, H5T_NATIVE_INT32, "32", data->getAs<types::Int32>());
314 case types::InternalType::ScilabInt64:
315 dataset = export_int(parent, name, H5T_NATIVE_INT64, "64", data->getAs<types::Int64>());
317 case types::InternalType::ScilabUInt8:
318 dataset = export_int(parent, name, H5T_NATIVE_UINT8, "u8", data->getAs<types::UInt8>());
320 case types::InternalType::ScilabUInt16:
321 dataset = export_int(parent, name, H5T_NATIVE_UINT16, "u16", data->getAs<types::UInt16>());
323 case types::InternalType::ScilabUInt32:
324 dataset = export_int(parent, name, H5T_NATIVE_UINT32, "u32", data->getAs<types::UInt32>());
326 case types::InternalType::ScilabUInt64:
327 dataset = export_int(parent, name, H5T_NATIVE_UINT64, "u64", data->getAs<types::UInt64>());
329 case types::InternalType::ScilabStruct:
330 dataset = export_struct(parent, name, data->getAs<types::Struct>());
332 case types::InternalType::ScilabPolynom:
333 dataset = export_poly(parent, name, data->getAs<types::Polynom>());
335 case types::InternalType::ScilabSparse:
336 dataset = export_sparse(parent, name, data->getAs<types::Sparse>());
338 case types::InternalType::ScilabSparseBool :
339 dataset = export_boolean_sparse(parent, name, data->getAs<types::SparseBool>());
341 case types::InternalType::ScilabCell:
342 dataset = export_cell(parent, name, data->getAs<types::Cell>());
344 case types::InternalType::ScilabVoid:
345 dataset = export_void(parent, name);
347 case types::InternalType::ScilabListUndefinedOperation:
348 dataset = export_undefined(parent, name);
350 case types::InternalType::ScilabMacro:
351 dataset = export_macro(parent, name, data->getAs<types::Macro>());
353 case types::InternalType::ScilabMacroFile:
355 types::MacroFile* pMF = data->getAs<types::MacroFile>();
356 dataset = export_macro(parent, name, pMF->getMacro());
359 case types::InternalType::ScilabHandle:
360 dataset = export_handles(parent, name, data->getAs<types::GraphicHandle>());
362 case types::InternalType::ScilabUserType:
363 dataset = export_usertype(parent, name, data->getAs<types::UserType>());
374 /*--------------------------------------------------------------------------*/
375 static int export_list(int parent, const std::string& name, types::List* data)
377 int size = data->getSize();
379 const char* type = nullptr;
380 switch (data->getType())
382 case types::InternalType::ScilabMList:
383 type = g_SCILAB_CLASS_MLIST;
385 case types::InternalType::ScilabTList:
386 type = g_SCILAB_CLASS_TLIST;
388 case types::InternalType::ScilabList:
389 type = g_SCILAB_CLASS_LIST;
395 //create node with list name
396 int dset = openList6(parent, name.data(), type);
398 for (int i = 0; i < size; ++i)
400 if (export_data(dset, std::to_string(i).data(), data->get(i)) == -1)
407 if (closeList6(dset) == -1)
413 /*--------------------------------------------------------------------------*/
414 static int export_double(int parent, const std::string& name, types::Double* data)
418 if (data->isComplex())
420 dataset = writeDoubleComplexMatrix6(parent, name.data(), data->getDims(), data->getDimsArray(), data->get(), data->getImg());
424 dataset = writeDoubleMatrix6(parent, name.data(), data->getDims(), data->getDimsArray(), data->get());
429 /*--------------------------------------------------------------------------*/
431 static int export_int(int parent, const std::string& name, int type, const char* prec, T* data)
433 return writeIntegerMatrix6(parent, name.data(), type, prec, data->getDims(), data->getDimsArray(), data->get());
435 /*--------------------------------------------------------------------------*/
436 static int export_string(int parent, const std::string& name, types::String* data)
438 int size = data->getSize();
439 wchar_t** s = data->get();
440 std::vector<char*> v(size);
442 //convert UTF16 strings to UTF8
443 for (int i = 0; i < size; ++i)
445 v[i] = wide_string_to_UTF8(s[i]);
448 int dset = writeStringMatrix6(parent, name.data(), data->getDims(), data->getDimsArray(), v.data());
451 for (int i = 0; i < size; ++i)
458 /*--------------------------------------------------------------------------*/
459 static int export_boolean(int parent, const std::string& name, types::Bool* data)
461 return writeBooleanMatrix6(parent, name.data(), data->getDims(), data->getDimsArray(), data->get());
463 /*--------------------------------------------------------------------------*/
464 static int export_struct(int parent, const std::string& name, types::Struct* data, const char* type)
466 //create a group with struct name
467 int dset = openList6(parent, name.data(), type);
468 //store struct dimensions
469 std::vector<int> dims = {1, data->getDims()};
470 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
476 int size = data->getSize();
480 if (closeList6(dset) == -1)
488 //create a node for fields references
489 int refs = openList6(dset, "__refs__", g_SCILAB_CLASS_STRUCT);
495 types::String* fields = data->getFieldNames();
496 int fieldCount = fields->getSize();
497 wchar_t** pfields = fields->get();
499 //save fields list in vector to keep order
500 export_string(dset, "__fields__", fields);
502 std::vector<hobj_ref_t> vrefs(size);
503 //fill main group with struct field name
504 for (int i = 0; i < fieldCount; ++i)
506 char* cfield = wide_string_to_UTF8(pfields[i]);
507 for (int j = 0; j < size; ++j)
510 types::InternalType* val = data->get(j)->get(pfields[i]);
512 std::string refname(cfield);
513 refname += "_" + std::to_string(j);
514 //export data in refs group
515 int ref = export_data(refs, refname, val);
516 //create reference of data
517 ret = addItemStruct6(refs, vrefs.data(), j, refname.data());
526 ret = writeStructField6(dset, cfield, data->getDims(), data->getDimsArray(), vrefs.data());
537 if (closeList6(refs) == -1)
542 if (closeList6(dset) == -1)
549 /*--------------------------------------------------------------------------*/
550 static int export_void(int parent, const std::string& name)
552 return writeVoid6(parent, name.data());
554 /*--------------------------------------------------------------------------*/
555 static int export_undefined(int parent, const std::string& name)
557 return writeUndefined6(parent, name.data());
559 /*--------------------------------------------------------------------------*/
560 static int export_poly(int parent, const std::string& name, types::Polynom* data)
562 //create a group with struct name
563 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_POLY);
564 //store struct dimensions
565 std::vector<int> dims = {1, data->getDims()};
566 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
572 //store variable name
573 std::vector<int> vardims = {1, 1};
574 char* varname = wide_string_to_UTF8(data->getVariableName().data());
575 ret = writeStringMatrix6(dset, "__varname__", 2, vardims.data(), &varname);
582 //create a node for fields references
583 int refs = openList6(dset, "__refs__", g_SCILAB_CLASS_POLY);
589 bool complex = data->isComplex();
590 int size = data->getSize();
591 std::vector<hobj_ref_t> vrefs(size);
592 types::SinglePoly** ss = data->get();
593 //fill main group with struct field name
594 for (int j = 0; j < size; ++j)
597 types::SinglePoly* val = ss[j];
598 //export data in refs group
599 std::vector<int> ssdims = {1, val->getSize()};
600 std::string polyname(std::to_string(j));
603 writeDoubleComplexMatrix6(refs, polyname.data(), 2, ssdims.data(), val->get(), val->getImg());
607 writeDoubleMatrix6(refs, polyname.data(), 2, ssdims.data(), val->get());
610 //create reference of data
611 ret = addItemStruct6(refs, vrefs.data(), j, polyname.data());
619 if (closeList6(refs) == -1)
624 if (closeList6(dset) == -1)
631 /*--------------------------------------------------------------------------*/
632 static int export_sparse(int parent, const std::string& name, types::Sparse* data)
634 int nnz = static_cast<int>(data->nonZeros());
635 int row = data->getRows();
636 //create a group with sparse name
637 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_SPARSE);
638 //store sparse dimensions
639 std::vector<int> dims = {1, data->getDims()};
640 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
646 //store numbers of non zero by rows.
647 std::vector<int> dimsnnz = {1, 1};
648 ret = writeIntegerMatrix6(dset, "__nnz__", H5T_NATIVE_INT32, "32", 2, dimsnnz.data(), &nnz);
656 int* inner = data->getInnerPtr(&innercount);
657 std::vector<int> dimsinner = {1, nnz};
658 ret = writeIntegerMatrix6(dset, "__inner__", H5T_NATIVE_INT32, "32", 2, dimsinner.data(), inner);
665 int* outer = data->getOuterPtr(&outercount);
666 std::vector<int> dimsouter = {1, outercount + 1};
667 ret = writeIntegerMatrix6(dset, "__outer__", H5T_NATIVE_INT32, "32", 2, dimsouter.data(), outer);
673 if (data->isComplex())
675 double* real = new double[nnz];
676 double* img = new double[nnz];
677 std::complex<double>* d = data->getImg();
678 for (int i = 0; i < nnz; ++i)
680 real[i] = d[i].real();
681 img[i] = d[i].imag();
685 std::vector<int> dimsdata = {1, nnz};
686 ret = writeDoubleComplexMatrix6(dset, "__data__", 2, dimsdata.data(), real, img);
696 std::vector<int> dimsdata = {1, nnz};
697 ret = writeDoubleMatrix6(dset, "__data__", 2, dimsdata.data(), data->get());
704 if (closeList6(dset) == -1)
712 /*--------------------------------------------------------------------------*/
713 static int export_boolean_sparse(int parent, const std::string& name, types::SparseBool* data)
715 int nnz = static_cast<int>(data->nbTrue());
716 int row = data->getRows();
717 //create a group with sparse name
718 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_BSPARSE);
719 //store sparse dimensions
720 std::vector<int> dims = {1, data->getDims()};
721 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
727 //store numbers of non zero by rows.
728 std::vector<int> dimsnnz = {1, 1};
729 ret = writeIntegerMatrix6(dset, "__nnz__", H5T_NATIVE_INT32, "32", 2, dimsnnz.data(), &nnz);
737 int* inner = data->getInnerPtr(&innercount);
738 std::vector<int> dimsinner = {1, nnz};
739 ret = writeIntegerMatrix6(dset, "__inner__", H5T_NATIVE_INT32, "32", 2, dimsinner.data(), inner);
746 int* outer = data->getOuterPtr(&outercount);
747 std::vector<int> dimsouter = {1, outercount + 1};
748 ret = writeIntegerMatrix6(dset, "__outer__", H5T_NATIVE_INT32, "32", 2, dimsouter.data(), outer);
754 if (closeList6(dset) == -1)
761 /*--------------------------------------------------------------------------*/
762 static int export_cell(int parent, const std::string& name, types::Cell* data)
764 //create a group with cell name
765 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_CELL);
766 //store cell dimensions
767 std::vector<int> dims = {1, data->getDims()};
768 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
774 //create a node for fields references
775 int refs = openList6(dset, "__refs__", g_SCILAB_CLASS_CELL);
781 int size = data->getSize();
782 types::InternalType** it = data->get();
783 std::vector<hobj_ref_t> vrefs(size);
784 for (int i = 0; i < size; ++i)
786 std::string refname(std::to_string(i));
787 int ref = export_data(refs, refname, it[i]);
791 if (closeList6(refs) == -1)
796 if (closeList6(dset) == -1)
804 static int export_handles(int parent, const std::string& name, types::GraphicHandle* data)
806 //create a group with cell name
807 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_HANDLE);
808 //store cell dimensions
809 std::vector<int> dims = {1, data->getDims()};
810 int ret = writeIntegerMatrix6(dset, "__dims__", H5T_NATIVE_INT32, "32", 2, dims.data(), data->getDimsArray());
816 //create a node for fields references
817 int refs = openList6(dset, "__refs__", g_SCILAB_CLASS_HANDLE);
824 int size = data->getSize();
825 long long* ll = data->get();
826 std::vector<hobj_ref_t> vrefs(size);
827 for (int i = 0; i < size; ++i)
830 int hl = getObjectFromHandle(static_cast<long>(ll[i]));
831 std::string refname(std::to_string(i));
832 if (export_handle(refs, refname, hl) == false)
845 static int export_macro(int parent, const std::string& name, types::Macro* data)
849 //create a group with macro name
850 int dset = openList6(parent, name.data(), g_SCILAB_CLASS_MACRO);
853 std::vector<char*> inputNames;
854 auto inputs = data->getInputs();
855 for (auto & input : *inputs)
857 inputNames.push_back(wide_string_to_UTF8(input->getSymbol().getName().data()));
861 dims[1] = static_cast<int>(inputNames.size());
862 writeStringMatrix6(dset, "inputs", 2, dims, inputNames.data());
864 for (auto & in : inputNames)
870 std::vector<char*> outputNames;
871 auto outputs = data->getOutputs();
872 for (auto & output : *outputs)
874 outputNames.push_back(wide_string_to_UTF8(output->getSymbol().getName().data()));
878 dims[1] = static_cast<int>(outputNames.size());
879 writeStringMatrix6(dset, "outputs", 2, dims, outputNames.data());
881 for (auto & in : outputNames)
887 ast::Exp* pExp = data->getBody();
888 ast::SerializeVisitor serialMacro(pExp);
890 unsigned char* serialAst = serialMacro.serialize();
891 //size if the buffer ( unsigned int )
892 unsigned int size = *((unsigned int*)serialAst);
896 writeIntegerMatrix6(dset, "body", H5T_NATIVE_UINT8, "u8", 2, dims, serialAst);
903 static int export_usertype(int parent, const std::string& name, types::UserType* data)
905 types::InternalType* it = data->save();
908 types::typed_list in;
911 types::typed_list out;
914 std::wstring wstFuncName = L"%" + data->getShortTypeStr() + L"_save";
918 types::Callable::ReturnValue ret = Overload::call(wstFuncName, in, 1, out);
920 if (ret != types::Callable::OK)
936 catch (const ast::InternalError& /*ie*/)
938 //overload does not exist
944 if (it->isUserType())
950 //create a struct around "usertype" to be able to restore it.
951 types::Struct* str = new types::Struct(1, 1);
952 types::SingleStruct* ss = str->get()[0];
955 ss->addField(L"type");
956 ss->addField(L"data");
958 //assign values to new fields
959 ss->set(L"type", new types::String(data->getShortTypeStr().data()));
960 ss->set(L"data", it);
962 int ret = export_struct(parent, name, str, g_SCILAB_CLASS_USERTYPE);
964 //protect data against delete