GNU Octave 11.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
ls-mat-subsys.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2025-2026 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include "byte-swap.h"
31
32#include "cdef-class.h"
33#include "cdef-utils.h"
34#include "error.h"
35#include "interpreter-private.h"
36#include "load-save.h"
37#include "ls-mat-subsys.h"
38#include "ov-classdef.h"
39
40static uint32_t MIN_FILEWRAPPER_VERSION = 2;
41static uint32_t MCOS_MAGIC_NUMBER = 0xDD000000;
42
43static inline bool
44is_valid_mcos_object (const octave_value& metadata)
45{
46 // Metadata must be N X 1 uint32 array, N>=3
47 if (! metadata.is_uint32_type ())
48 {
49 if (metadata.isstruct () && metadata.is_scalar_type ())
50 {
51 // Metadata for enumeration class objects are stored as structs
52 // with a field "EnumerationInstanceTag" set to 0xDD000000.
53
54 const octave_scalar_map& md = metadata.scalar_map_value ();
55 if (md.contains ("EnumerationInstanceTag"))
56 {
57 const octave_value& enum_tag = md.contents ("EnumerationInstanceTag");
58 if (enum_tag.is_uint32_type () && enum_tag.is_scalar_type ()
59 && static_cast<uint32_t> (enum_tag.uint32_scalar_value ())
60 == MCOS_MAGIC_NUMBER)
61 warning_with_id ("Octave:load:classdef-not-supported",
62 "load: MATLAB enumeration class object cannot be loaded. "
63 "Returning as %s",
64 metadata.class_name ().c_str ());
65 }
66 }
67
68 return false;
69 }
70
71 const uint32NDArray& md = metadata.uint32_array_value ();
72 if (md.ndims () != 2)
73 return false;
74
75 dim_vector dv = md.dims ();
76 if (dv(1) != 1)
77 return false;
78
79 if (md.numel () < 3)
80 return false;
81
82 // first value must be 0xDD000000
83 if (static_cast<uint32_t> (md(0, 0)) != MCOS_MAGIC_NUMBER)
84 return false;
85
86 return true;
87}
88
90
91static octave_value
92search_mcos_classdef_objects (const octave_value& val, bool save_mode)
93{
94 if (save_mode && val.is_classdef_object ())
95 {
96 subsystem_handler *sh = octave::__get_load_save_system__ ().get_subsystem_handler ();
97 return octave_value (sh->set_mcos_object_metadata (val));
98 }
99 else if (! save_mode && is_valid_mcos_object (val))
100 {
101 return load_mcos_object (val);
102 }
103
104 // iterate through cell arrays
105 if (val.iscell ())
106 {
107 Cell cv = val.cell_value ();
108 for (octave_idx_type i = 0; i < cv.numel (); i++)
109 cv(i) = search_mcos_classdef_objects (cv(i), save_mode);
110
111 return cv;
112 }
113
114 // iterate through struct arrays
115 if (val.isstruct ())
116 {
117 if (val.is_scalar_type ())
118 {
120 for (auto it = mv.begin (); it != mv.end (); ++it)
121 {
122 std::string field_name = mv.key (it);
123 octave_value field_val = mv.contents (it);
124 mv.assign (field_name,
125 search_mcos_classdef_objects (field_val, save_mode));
126 }
127 return octave_value (mv);
128 }
129 else
130 {
131 octave_map mv = val.map_value ();
132 string_vector field_names = mv.fieldnames ();
133
134 for (octave_idx_type field_idx = 0; field_idx < field_names.numel ();
135 field_idx++)
136 {
137 std::string field_name = field_names[field_idx];
138 Cell field_values = mv.contents (field_name);
139
140 for (octave_idx_type i = 0; i < field_values.numel (); i++)
141 field_values(i) = search_mcos_classdef_objects (field_values(i),
142 save_mode);
143
144 mv.assign (field_name, field_values);
145 }
146
147 return octave_value (mv);
148 }
149 }
150
151 return val;
152}
153
155load_mcos_object (const octave_value& objmetadata, bool as_struct)
156{
157 // Object metadata is a Nx1 uint32 array with the following structure:
158 // [0xDD000000, objdims, dim1, dim2, ..., dimN,
159 // object_id1, object_id2, ..., object_idN,
160 // class_id]
161
162 octave::load_save_system& lss = octave::__get_load_save_system__ ();
163 octave::subsystem_handler *sh = lss.get_subsystem_handler ();
164
165 const octave_uint32 *dt = objmetadata.uint32_array_value ().data ();
166 if (static_cast<uint32_t>(dt[0]) != MCOS_MAGIC_NUMBER)
167 {
168 warning_with_id ("Octave:load:invalid-mcos-object",
169 "load: invalid MCOS object metadata. Returning as %s",
170 objmetadata.class_name ().c_str ());
171 return objmetadata;
172 }
173 int32_t objdims = dt[1];
174
175 dim_vector dv;
176 dv.resize (objdims);
177 for (octave_idx_type i = 0; i < objdims; i++)
178 dv(i) = dt[2 + i];
179
180 int nobjects = dv.numel ();
181 std::vector<std::tuple<octave_map, uint32_t, bool>> m (nobjects);
182
183 for (octave_idx_type i = 0; i < nobjects; i++)
184 std::get<uint32_t> (m[i]) = dt[2 + objdims + i];
185
186 uint32_t class_id = dt[2 + objdims + nobjects];
187 std::string classname = sh->get_class_name (class_id);
188
189 bool skip_constructor = true;
190 octave::cdef_class cls;
191 if (! as_struct)
192 {
193 // "function_handle_workspace" objects contain data of the workspace
194 // context of function handles. Return it as structure.
195 // FIXME: Add compatible implementation of this class.
196 if (classname == "function_handle_workspace")
197 as_struct = true;
198 else
199 {
200 cls = octave::lookup_class (classname, false, true);
201 if (! cls.ok ())
202 {
203 warning_with_id ("Octave:load:classdef-not-found",
204 "load: classdef not found. Element loaded as %s",
205 objmetadata.class_name ().c_str ());
206
207 return objmetadata;
208 }
209
210 skip_constructor = ! cls.get ("ConstructOnLoad").bool_value ();
211 }
212 }
213
214 for (octave_idx_type i = 0; i < nobjects; i++)
215 {
216 uint32_t obj_id = std::get<uint32_t> (m[i]);
217 if (! obj_id)
218 {
219 // FIXME: Properly support deleted objects.
220 warning_with_id ("Octave:load:deleted-object",
221 "load: object of class '%s' was deleted in MATLAB "
222 "and cannot be loaded. Returning metadata",
223 classname.c_str ());
224 return objmetadata;
225 }
226 if (! lss.is_mcos_object_cache_entry (obj_id))
227 {
228 if (! as_struct)
229 {
230 // put (scalar) object in cache for this object ID
231 octave_value ovc
232 = cls.construct (octave_value_list (), skip_constructor);
233 lss.set_mcos_object_cache_entry (obj_id, ovc);
234 }
235
236 auto [saveobj_type, obj_type_id, obj_dep_id]
237 = sh->get_object_dependencies (obj_id);
238
239 std::get<octave_map> (m[i])
240 = sh->get_object_properties (obj_type_id, class_id, saveobj_type,
241 as_struct);
242 std::get<bool> (m[i]) = saveobj_type;
243
244 if (! as_struct && sh->check_dyn_props (obj_dep_id))
245 warning_with_id ("Octave:load:dynamic-properties-not-supported",
246 "load: dynamic properties cannot be loaded and "
247 "will be skipped ");
248 }
249 }
250
251 if (as_struct)
252 {
253 OCTAVE_LOCAL_BUFFER (octave_map, map_list, nobjects);
254
255 octave_idx_type j = 0;
256 for (octave_idx_type i = 0; i < nobjects; i++)
257 {
258 if ((std::get<octave_map> (m[i])).isfield ("any"))
259 {
260 // object was saved with overloaded "saveobj" method that
261 // returns a different type
262 octave_value any_val
263 = ((std::get<octave_map> (m[i])).getfield ("any"))(0);
264 // FIXME: This is specifically for loading
265 // "function_handle_workspace" objects saved with MATLAB.
266 // Do we need to be more generic?
267 if (any_val.iscell () && any_val.numel () > 1)
268 {
269 Cell any_cell = any_val.cell_value ();
270 map_list[j] = any_cell(1).scalar_map_value ();
271 }
272 }
273 else
274 map_list[j] = std::get<octave_map> (m[i]);
275
276 j++;
277 }
278
279 if (j == 1)
280 return map_list[0];
281 else
282 return octave_map::cat (-1, nobjects, map_list);
283 }
284 else
285 {
286 // create object with loaded data
287 octave_value obj = cls.construct (octave_value_list (), true);
288 octave_classdef *objdef = obj.classdef_object_value ();
289 objdef->loadobj (m, dv);
290
291 return obj;
292 }
293}
294
295bool
297{
298 if (! subsys.isstruct ())
299 return false;
300
301 const octave_scalar_map& subsys_map = subsys.scalar_map_value ();
302
303 if (subsys_map.contains ("MCOS"))
304 {
305 const octave_value& mcos_data = subsys_map.contents ("MCOS");
306 if (mcos_data.is_defined () && ! mcos_data.isempty ()
307 && ! read_filewrapper (mcos_data.cell_value (), swap))
308 return false;
309 }
310
311 if (subsys_map.contains ("java"))
312 {
313 const octave_value& java_data = subsys_map.contents ("java");
314 if (java_data.is_defined () && ! java_data.isempty ())
315 m_type_java = java_data;
316 }
317
318 if (subsys_map.contains ("handle"))
319 {
320 const octave_value& handle_data = subsys_map.contents ("handle");
321 if (handle_data.is_defined () && ! handle_data.isempty ())
322 m_type_handle = handle_data;
323 }
324
325 return true;
326}
327
328bool
329subsystem_handler::read_filewrapper (const Cell& fwrap_data, bool swap)
330{
331 const octave_value& fwrap_metadata = fwrap_data(0, 0);
332 if (! fwrap_metadata.is_uint8_type ())
333 return false;
334
335 const uint8NDArray& fwrap_metadata_array = fwrap_metadata.uint8_array_value ();
336
337 // check version compatibility
338 uint32_t version;
339 std::memcpy (&version, fwrap_metadata_array.data (), sizeof (version));
340 if (swap)
341 swap_bytes<4> (&version, 1);
342 if (version > m_filewrapper_version || version < MIN_FILEWRAPPER_VERSION)
343 error ("load: filewrapper version %u is not supported. MCOS objects cannot be loaded.",
344 version);
345
346 // get number of unique property names and class names
347 std::memcpy (&m_num_names, fwrap_metadata_array.data () + 4,
348 sizeof (m_num_names));
349 if (swap)
350 swap_bytes<4> (&m_num_names, 1);
351
352 // read region offsets
353 for (octave_idx_type i = 0; i < 8; i++)
354 {
355 std::memcpy (m_region_offsets.data () + i,
356 fwrap_metadata_array.data () + 8 + i * 4,
357 sizeof (int32_t));
358 if (swap)
359 swap_bytes<4> (&m_region_offsets[i], 1);
360 }
361
362 // read property names and class names
363 // stored as list of null terminated integers
364 m_prop_class_names.resize (m_num_names);
365 size_t name_offset = 40;
366 for (uint32_t i = 0; i < m_num_names; i++)
367 {
368 size_t start_offset = name_offset;
369 while (static_cast<uint8_t> (fwrap_metadata_array(name_offset)) != 0)
370 name_offset++;
371
372 std::string name;
373 for (size_t j = start_offset; j < name_offset; j++)
374 name += static_cast<char> (fwrap_metadata_array(j));
375
376 m_prop_class_names[i] = name;
377 name_offset++; // skip the null terminator
378 }
379
380 // read property values
381 // Property values are stored in fwrap_data(2:end-X) where:
382 // X = [1,2,3] for Filewrapper version [2,3,4]
383 int prop_vals_idx_end = version - 1; // simple hack for above mapping
384 // ignore first 2 entries as well
385 octave_idx_type prop_vals_size = fwrap_data.numel () - prop_vals_idx_end - 2;
386 if (prop_vals_size > 0)
387 {
388 m_fwrap_prop_vals.resize (prop_vals_size);
389 for (octave_idx_type i = 0; i < prop_vals_size; i++)
390 m_fwrap_prop_vals[i] = fwrap_data(2 + i);
391 }
392 else
393 m_fwrap_prop_vals.clear (); // mark undefined
394
395 // Default values are stored in fwrap_data(end).
396 m_fwrap_default_vals = fwrap_data(fwrap_data.numel () - 1).cell_value ();
397
398 // read m_class_name_refs
399 // contains list of indices to classname and namespace qualifier (if any) for
400 // each class
401 size_t class_name_size = (m_region_offsets[1] - m_region_offsets[0]) / 4;
402 m_class_name_refs.resize (class_name_size);
403 for (size_t i = 0; i < class_name_size; i++)
404 {
405 uint32_t val;
406 std::memcpy (&val,
407 fwrap_metadata_array.data () + m_region_offsets[0] + i * 4,
408 sizeof (val));
409 if (swap)
410 swap_bytes<4> (&val, 1);
411 m_class_name_refs[i] = val;
412 }
413
414 // read m_object_id_refs
415 // contains class ID, object_type_ID, dependency_ID for each object
416 size_t object_id_size = (m_region_offsets[3] - m_region_offsets[2]) / 4;
417 m_object_id_refs.resize (object_id_size);
418 for (size_t i = 0; i < object_id_size; i++)
419 {
420 uint32_t val;
421 std::memcpy (&val,
422 fwrap_metadata_array.data () + m_region_offsets[2] + i * 4,
423 sizeof (val));
424 if (swap)
425 swap_bytes<4> (&val, 1);
426 m_object_id_refs[i] = val;
427 }
428
429 // read m_object_prop_fields
430 // contains blocks of property name indexes and values for each object
431 size_t object_prop_size = (m_region_offsets[4] - m_region_offsets[3]) / 4;
432 m_object_prop_fields.resize (object_prop_size);
433 for (size_t i = 0; i < object_prop_size; i++)
434 {
435 uint32_t val;
436 std::memcpy (&val,
437 fwrap_metadata_array.data () + m_region_offsets[3] + i * 4,
438 sizeof (val));
439 if (swap)
440 swap_bytes<4> (&val, 1);
441 m_object_prop_fields[i] = val;
442 }
443
444 // read m_saveobj_prop_fields
445 // contains value index for each object with custom saveobj return types
446 size_t saveobj_prop_size = (m_region_offsets[2] - m_region_offsets[1]) / 4;
447 m_saveobj_prop_fields.resize (saveobj_prop_size);
448 for (size_t i = 0; i < saveobj_prop_size; i++)
449 {
450 uint32_t val;
451 std::memcpy (&val,
452 fwrap_metadata_array.data () + m_region_offsets[1] + i * 4,
453 sizeof (val));
454 if (swap)
455 swap_bytes<4> (&val, 1);
456 m_saveobj_prop_fields[i] = val;
457 }
458
459 // read m_dynamic_prop_refs
460 // lists dynamic properties (if any) for each object dependency ID
461 size_t dynamic_prop_size = (m_region_offsets[5] - m_region_offsets[4]) / 4;
462 m_dynamic_prop_refs.resize (dynamic_prop_size);
463 for (size_t i = 0; i < dynamic_prop_size; i++)
464 {
465 uint32_t val;
466 std::memcpy (&val,
467 fwrap_metadata_array.data () + m_region_offsets[4] + i * 4,
468 sizeof (val));
469 if (swap)
470 swap_bytes<4> (&val, 1);
471 m_dynamic_prop_refs[i] = val;
472 }
473
474 return true;
475}
476
477std::string
478subsystem_handler::get_class_name (const uint32_t class_id)
479{
480 // (namespace_idx, classname_idx, X, X)
481 // ordered by class ID
482
483 constexpr int block_size = 4;
484 uint32_t namespace_idx = m_class_name_refs[class_id * block_size];
485 uint32_t class_idx = m_class_name_refs[class_id * block_size + 1];
486
487 std::string classname;
488 if (namespace_idx)
489 classname = m_prop_class_names[namespace_idx - 1] + ".";
490 classname += m_prop_class_names[class_idx - 1];
491 return classname;
492}
493
494std::tuple<bool, uint32_t, uint32_t>
496{
497 // (class_ID, X, X, saveobj_ID, normal_obj_ID, dependency_ID)
498 // ordered by object ID
499
500 constexpr int block_size = 6;
501 bool saveobj_type = true;
502
503 uint32_t obj_dep_id = m_object_id_refs[obj_id * block_size + 5];
504 uint32_t obj_con_id = m_object_id_refs[obj_id * block_size + 3];
505 if (! obj_con_id)
506 {
507 saveobj_type = false;
508 obj_con_id = m_object_id_refs[obj_id * block_size + 4];
509 }
510
511 return {saveobj_type, obj_con_id, obj_dep_id};
512}
513
516 const uint32_t class_id,
517 const bool saveobj_ret_type,
518 bool as_struct)
519{
520 // (nprops, prop1_name_idx, prop1_val_type, prop1_val, prop2_name_idx ...)
521 // ordered by object type ID
522
523 const std::vector<uint32_t>& offset_fields
524 = saveobj_ret_type ? m_saveobj_prop_fields : m_object_prop_fields;
525 const uint32_t* ptr = offset_fields.data();
526 const uint32_t* end_ptr = ptr + offset_fields.size();
527
528 // skip to the correct obj_type_id block
529 constexpr int block_size = 3;
530 for (uint32_t current_id = 0; current_id < obj_type_id; current_id++)
531 {
532 uint32_t nprops = *ptr++;
533 ptr += nprops * block_size;
534 ptr += (1 + (nprops * block_size)) % 2; // padding
535 if (ptr >= end_ptr)
536 error ("Could not load file");
537 }
538
539 octave_idx_type nprops = *ptr++;
540 octave_map prop_vals;
541 if (! as_struct)
542 {
543 octave_value& default_vals = m_fwrap_default_vals(class_id);
544 default_vals = search_mcos_classdef_objects (default_vals, false);
545 prop_vals = default_vals.map_value ();
546 }
547
548 for (octave_idx_type i = 0; i < nprops && ptr + 2 < end_ptr; i++)
549 {
550 uint32_t prop_name_idx = *ptr++;
551 uint32_t prop_val_type = *ptr++;
552 uint32_t prop_val_idx = *ptr++;
553
554 const std::string& prop_name = m_prop_class_names[prop_name_idx - 1];
555
556 switch (prop_val_type)
557 {
558 case 0:
559 // Property value is enum/string reference
560 {
561 warning_with_id ("Octave:load:enum-string-ref",
562 "load: property '%s' is an enum reference. "
563 "This may not be supported and is read as a string.",
564 prop_name.c_str ());
565 const std::string& prop_val = m_prop_class_names[prop_val_idx - 1];
566 prop_vals.assign (prop_name, octave_value (prop_val));
567 }
568 break;
569
570 case 1:
571 // Property value points to a cell in m_fwrap_prop_vals
572 {
573 octave_value& prop_val = m_fwrap_prop_vals[prop_val_idx];
574 prop_val = search_mcos_classdef_objects (prop_val, false);
575 prop_vals.assign (prop_name, prop_val);
576 }
577 break;
578
579 case 2:
580 // prop_val_idx is the property value itself? (Not sure)
581 {
582 prop_vals.assign (prop_name, octave_value (prop_val_idx));
583 }
584 break;
585
586 default:
587 warning ("load: unknown property value type %u for property '%s'",
588 prop_val_type, prop_name.c_str ());
589 break;
590 }
591 }
592
593 return prop_vals;
594}
595
596bool
597subsystem_handler::check_dyn_props (const uint32_t obj_dep_id)
598{
599 // (ndynprops, dynprop1_obj_id, dynprop2_obj_id ...)
600 // ordered by dependency ID
601
602 // FIXME: Actually implement support when Octave supports dynamicprops.
603 // For now, only check if exists.
604
605 // Newer filewrapper versions do not write metadata if no dynprops are present.
606 // However, older versions write blocks of zeros.
607 if (m_dynamic_prop_refs.empty () || (! obj_dep_id))
608 return false; // no dynamic properties present
609
610 const uint32_t *ptr = m_dynamic_prop_refs.data ();
611 const uint32_t *end_ptr = ptr + m_dynamic_prop_refs.size ();
612
613 bool has_dyn_props = false;
614 for (uint32_t current_id = 0; current_id < obj_dep_id; current_id++)
615 {
616 uint32_t nprops = *ptr++;
617 ptr += nprops;
618 ptr += (1 + nprops) % 2; // padding
619 if (ptr >= end_ptr)
620 return false;
621 }
622
623 uint32_t nprops = *ptr++;
624 if (nprops)
625 has_dyn_props = true;
626 return has_dyn_props;
627}
628
629std::pair<std::string, std::string>
630subsystem_handler::parse_class_name (const std::string& full_class_name)
631{
632 std::string namespace_name;
633 std::string class_name;
634
635 // separate namespace from class name
636 size_t last_dot_pos = full_class_name.find_last_of ('.');
637 if (last_dot_pos != std::string::npos)
638 {
639 namespace_name = full_class_name.substr (0, last_dot_pos);
640 class_name = full_class_name.substr (last_dot_pos + 1);
641 }
642 else
643 {
644 namespace_name = "";
645 class_name = full_class_name;
646 }
647
648 return std::make_pair (namespace_name, class_name);
649}
650
651uint32_t
652subsystem_handler::get_name_index (const std::string& name)
653{
654 for (octave_idx_type i = 0; i < m_prop_class_names.numel (); i++)
655 {
656 if (m_prop_class_names[i] == name)
657 return i + 1; // names are 1-indexed
658 }
659
660 m_prop_class_names.append (name);
661 m_num_names = m_prop_class_names.numel ();
662 return m_num_names;
663}
664
665uint32_t
666subsystem_handler::get_or_create_class_id (const std::string& namespace_name,
667 const std::string& class_name)
668{
669 uint32_t current_class_id = 1;
670 bool class_exists = false;
671
672 // search through existing m_class_name_refs to find this class
673 for (size_t i = 0; i < m_class_name_refs.size(); i += 4)
674 {
675 uint32_t existing_namespace_idx = m_class_name_refs[i];
676 uint32_t existing_class_idx = m_class_name_refs[i + 1];
677
678 std::string existing_namespace
679 = ((existing_namespace_idx > 0)
680 ? m_prop_class_names[existing_namespace_idx - 1] : "");
681 std::string existing_class = m_prop_class_names[existing_class_idx - 1];
682
683 if (existing_namespace == namespace_name && existing_class == class_name)
684 {
685 class_exists = true;
686 break;
687 }
688 current_class_id++;
689 }
690
691 if (! class_exists)
692 {
693 m_class_id_counter++;
694
695 // individual names can be shared
696 uint32_t namespace_idx = (namespace_name.empty()
697 ? 0 : get_name_index (namespace_name));
698 uint32_t class_name_idx = get_name_index (class_name);
699
700 m_class_name_refs.push_back (namespace_idx);
701 m_class_name_refs.push_back (class_name_idx);
702 m_class_name_refs.push_back (0);
703 m_class_name_refs.push_back (0);
704 }
705
706 return current_class_id;
707}
708
710subsystem_handler::create_metadata_array (const dim_vector& obj_dims,
711 const std::vector<uint32_t>& object_ids,
712 uint32_t class_id)
713{
714 uint32_t obj_ndims = obj_dims.ndims ();
715 uint32_t nobjects = obj_dims.numel ();
716
717 // metadata format: [0xDD000000, objdims, dim1, dim2, ..., dimN,
718 // object_id1, object_id2, ..., object_idN, class_id]
719 uint32NDArray metadata (dim_vector (3 + obj_ndims + nobjects, 1));
720
721 metadata(0) = MCOS_MAGIC_NUMBER; // magic number for MCOS objects
722 metadata(1) = obj_ndims;
723
724 for (uint32_t i = 0; i < obj_ndims; i++)
725 metadata(2 + i) = obj_dims(i);
726
727 for (uint32_t i = 0; i < nobjects; i++)
728 metadata(2 + obj_ndims + i) = object_ids[i];
729
730 metadata(2 + obj_ndims + nobjects) = class_id;
731
732 return metadata;
733}
734
735void
736subsystem_handler::process_object_properties (const std::vector<std::tuple<octave_map, uint32_t, bool>>& saveobj_data,
737 const std::vector<bool>& is_new,
738 uint32_t class_id)
739{
740 // Save positions for insertion at the end
741 size_t object_id_refs_pos = m_object_id_refs.size ();
742 size_t object_prop_fields_pos = m_object_prop_fields.size ();
743 size_t saveobj_prop_fields_pos = m_saveobj_prop_fields.size ();
744
745 // local storage for this batch of objects
746 std::vector<uint32_t> local_object_id_refs;
747 std::vector<uint32_t> local_object_prop_fields;
748 std::vector<uint32_t> local_saveobj_prop_fields;
749 std::vector<uint32_t> local_dynamic_prop_refs;
750
751 for (size_t obj_idx = 0; obj_idx < saveobj_data.size (); obj_idx++)
752 {
753 const auto& obj_tuple = saveobj_data[obj_idx];
754 const octave_map& prop_map = std::get<octave_map> (obj_tuple);
755 bool saveobj_ret_type = std::get<bool> (obj_tuple);
756
757 if (is_new[obj_idx])
758 {
759 uint32_t saveobj_object_id = 0;
760 uint32_t normal_object_id = 0;
761 std::vector<uint32_t>& target_prop_fields
762 = (saveobj_ret_type ? local_saveobj_prop_fields
763 : local_object_prop_fields);
764
765 if (saveobj_ret_type)
766 saveobj_object_id = ++m_saveobj_object_counter;
767 else
768 normal_object_id = ++m_normal_object_counter;
769
770 // TODO: Add dependency IDs
771 local_object_id_refs.push_back (class_id);
772 local_object_id_refs.push_back (0);
773 local_object_id_refs.push_back (0);
774 local_object_id_refs.push_back (saveobj_object_id);
775 local_object_id_refs.push_back (normal_object_id);
776 local_object_id_refs.push_back (0);
777
778 string_vector prop_names = prop_map.fieldnames ();
779 octave_idx_type num_props = prop_names.numel ();
780 target_prop_fields.push_back (num_props);
781
782 for (octave_idx_type prop_idx = 0; prop_idx < num_props; prop_idx++)
783 {
784 std::string prop_name = prop_names[prop_idx];
785 octave_value prop_value = (prop_map.contents (prop_name))(0);
786
787 prop_value = search_mcos_classdef_objects (prop_value, true);
788
789 uint32_t field_name_idx = get_name_index (prop_name);
790 uint32_t cell_number_idx = m_fwrap_prop_vals.size ();
791 m_fwrap_prop_vals.push_back (prop_value);
792
793 // (field_name_idx, field_val_type, cell_idx)
794 target_prop_fields.push_back (field_name_idx);
795 target_prop_fields.push_back (1);
796 target_prop_fields.push_back (cell_number_idx);
797 }
798
799 // TODO: Parse for different property types
800
801 if ((num_props * 3 + 1) % 2 != 0)
802 target_prop_fields.push_back (0); // padding
803
804 // dummy values for compatibility
805 local_dynamic_prop_refs.push_back (0);
806 local_dynamic_prop_refs.push_back (0);
807 }
808 }
809
810 // Insert at saved positions instead of appending to end
811 m_object_id_refs.insert (m_object_id_refs.begin () + object_id_refs_pos,
812 local_object_id_refs.begin (),
813 local_object_id_refs.end ());
814 m_object_prop_fields.insert (m_object_prop_fields.begin () + object_prop_fields_pos,
815 local_object_prop_fields.begin (),
816 local_object_prop_fields.end ());
817 m_saveobj_prop_fields.insert (m_saveobj_prop_fields.begin () + saveobj_prop_fields_pos,
818 local_saveobj_prop_fields.begin (),
819 local_saveobj_prop_fields.end ());
820 m_dynamic_prop_refs.insert (m_dynamic_prop_refs.end (),
821 local_dynamic_prop_refs.begin (),
822 local_dynamic_prop_refs.end ());
823}
824
827{
828 std::string full_class_name = obj.class_name ();
829 auto [namespace_name, class_name] = parse_class_name (full_class_name);
830
831 uint32_t current_class_id = get_or_create_class_id (namespace_name, class_name);
832
833 std::vector<bool> is_new (obj.numel ());
834 std::vector<std::tuple<octave_map, uint32_t, bool>> saveobj_data
835 = obj.classdef_object_value ()->saveobj (is_new);
836
837 dim_vector obj_dims = obj.dims ();
838 std::vector<uint32_t> actual_object_ids;
839 actual_object_ids.reserve (obj_dims.numel ());
840
841 for (const auto& obj_tuple : saveobj_data)
842 {
843 uint32_t obj_id = std::get<uint32_t> (obj_tuple);
844 actual_object_ids.push_back (obj_id);
845 }
846
847
848 process_object_properties (saveobj_data, is_new, current_class_id);
849
850 return create_metadata_array (obj_dims, actual_object_ids, current_class_id);
851}
852
855{
856 // helper to write data for object ID = 0
857 // Note: MATLAB has values for some kind of placeholder object with ID = 0
858 auto write_object_id_0 = [](uint8_t*& ptr, int count) -> void
859 {
860 uint32_t zero = 0;
861 for (int i = 0; i < count; i++)
862 {
863 std::memcpy (ptr, &zero, 4);
864 ptr += 4;
865 }
866 };
867
868 // calculate region offsets
869 size_t current_offset = 40; // start after header (version + m_num_names + 8 offsets)
870
871 for (octave_idx_type i = 0; i < m_prop_class_names.numel (); i++)
872 current_offset += m_prop_class_names[i].length () + 1; // +1 for null terminator
873
874 // padding
875 size_t padding_length = 0;
876 if (current_offset % 8 != 0)
877 {
878 padding_length = 8 - (current_offset % 8);
879 current_offset += padding_length;
880 }
881
882 m_region_offsets[0] = current_offset; // class_name metadata
883
884 current_offset += (m_class_name_refs.size () + 4) * 4;
885 m_region_offsets[1] = current_offset; // saveobj prop metadata
886
887 current_offset += m_saveobj_prop_fields.size () * 4;
888 if (m_saveobj_prop_fields.size () > 0)
889 current_offset += 8;
890 m_region_offsets[2] = current_offset; // object id metadata
891
892 current_offset += (m_object_id_refs.size () + 6) * 4;
893 m_region_offsets[3] = current_offset; // object prop metadata
894
895 current_offset += m_object_prop_fields.size () * 4;
896 if (m_object_prop_fields.size () > 0)
897 current_offset += 8;
898 m_region_offsets[4] = current_offset; // dynamicprop metadata
899
900 // Offsets 5-7: Unknown purposes
901 current_offset += m_dynamic_prop_refs.size () * 4;
902 current_offset += 8;
903 m_region_offsets[5] = current_offset;
904 m_region_offsets[6] = current_offset;
905 current_offset += 8;
906 m_region_offsets[7] = current_offset; // end of metadata
907
908 uint32_t& metadata_size = m_region_offsets[7];
909
910 // metadata format:
911 // version, m_num_names, region offsets, names
912 // class name metadata, saveobj prop metadata
913 // object id metadata, obj prop metadata
914 // dynamicprop metadata + unknowns
915 uint8NDArray metadata (dim_vector (metadata_size, 1));
916 uint8_t* metadata_ptr = reinterpret_cast<uint8_t*> (metadata.rwdata ());
917
918 uint32_t version = m_filewrapper_version;
919 std::memcpy (metadata_ptr, &version, 4);
920 metadata_ptr += 4;
921
922 std::memcpy (metadata_ptr, &m_num_names, 4);
923 metadata_ptr += 4;
924
925 for (int i = 0; i < 8; i++)
926 {
927 uint32_t offset_value = static_cast<uint32_t> (m_region_offsets[i]);
928 std::memcpy (metadata_ptr, &offset_value, 4);
929 metadata_ptr += 4;
930 }
931
932 for (octave_idx_type i = 0; i < m_prop_class_names.numel (); i++)
933 {
934 const std::string& name = m_prop_class_names[i];
935 std::memcpy (metadata_ptr, name.c_str (), name.length ());
936 metadata_ptr += name.length ();
937 *metadata_ptr++ = 0; // null terminator
938 }
939 for (size_t i = 0; i < padding_length; i++)
940 *metadata_ptr++ = 0; // padding
941
942 write_object_id_0 (metadata_ptr, 4);
943 for (size_t i = 0; i < m_class_name_refs.size (); i++)
944 {
945 uint32_t value = m_class_name_refs[i];
946 std::memcpy (metadata_ptr, &value, 4);
947 metadata_ptr += 4;
948 }
949
950 if (m_saveobj_prop_fields.size () > 0)
951 write_object_id_0 (metadata_ptr, 2); // slot 0 padding
952 for (size_t i = 0; i < m_saveobj_prop_fields.size (); i++)
953 {
954 uint32_t value = m_saveobj_prop_fields[i];
955 std::memcpy (metadata_ptr, &value, 4);
956 metadata_ptr += 4;
957 }
958
959 write_object_id_0 (metadata_ptr, 6); // slot 0 padding
960 for (size_t i = 0; i < m_object_id_refs.size (); i++)
961 {
962 uint32_t value = m_object_id_refs[i];
963 std::memcpy (metadata_ptr, &value, 4);
964 metadata_ptr += 4;
965 }
966
967 if (m_object_prop_fields.size () > 0)
968 write_object_id_0 (metadata_ptr, 2); // slot 0 padding
969 for (size_t i = 0; i < m_object_prop_fields.size (); i++)
970 {
971 uint32_t value = m_object_prop_fields[i];
972 std::memcpy (metadata_ptr, &value, 4);
973 metadata_ptr += 4;
974 }
975
976 write_object_id_0 (metadata_ptr, 2); // slot 0 padding
977 for (size_t i = 0; i < m_dynamic_prop_refs.size (); i++)
978 {
979 uint32_t value = m_dynamic_prop_refs[i];
980 std::memcpy (metadata_ptr, &value, 4);
981 metadata_ptr += 4;
982 }
983
984 // write 8 bytes of zeros for region_offset[6]
985 for (int i = 0; i < 8; i++)
986 *metadata_ptr++ = 0;
987
988 return metadata;
989}
990
991void
993{
994 if (! m_class_id_counter)
995 return;
996
997 // create cell array with size = m_fwrap_prop_vals.size () + 2 + 3
998 // (for the additional cells)
999 octave_idx_type cell_size = m_fwrap_prop_vals.size () + 5;
1000 Cell result (dim_vector (cell_size, 1));
1001
1002 result(0) = create_filewrapper_metadata ();
1003 result(1) = octave_value (Matrix ()); // empty matrix for some reason
1004
1005 // set remaining cells to property values
1006 for (size_t i = 0; i < m_fwrap_prop_vals.size (); i++)
1007 result(i + 2) = m_fwrap_prop_vals[i];
1008
1009 // unknown cells
1010 // These contain some class-level metadata
1011 // typically a cell array of empty 1x0 structs for each class ID
1012 octave_idx_type base_idx = m_fwrap_prop_vals.size () + 2;
1013 uint32_t num_class_ids = m_class_id_counter + 1;
1014
1015 Cell u3_structs (dim_vector (num_class_ids, 1));
1016 for (uint32_t i = 0; i < num_class_ids; i++)
1017 u3_structs(i) = octave_value (octave_map (dim_vector (1, 0)));
1018 result(base_idx) = octave_value (u3_structs);
1019
1020 int32NDArray u2_zeros (dim_vector (num_class_ids, 1));
1021 for (uint32_t i = 0; i < num_class_ids; i++)
1022 u2_zeros(i) = 0;
1023 result(base_idx + 1) = octave_value (u2_zeros);
1024
1025 // contains default values - not separately tagged in Octave
1026 m_fwrap_default_vals.resize (dim_vector (num_class_ids, 1));
1027 for (uint32_t i = 0; i < num_class_ids; i++)
1028 m_fwrap_default_vals(i) = octave_value (octave_map (dim_vector (1, 0)));
1029 result(base_idx + 2) = octave_value (m_fwrap_default_vals);
1030
1031 // storing only because save -> save_mat5_element_length which needs the
1032 // actual contents to write nbytes
1033 m_serialized_data = result;
1034}
1035
1036OCTAVE_END_NAMESPACE(octave)
void swap_bytes< 4 >(void *ptr)
Definition byte-swap.h:63
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition Array-base.h:529
int ndims() const
Size of the specified dimension.
Definition Array-base.h:701
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
const T * data() const
Size of the specified dimension.
Definition Array-base.h:687
T * rwdata()
Size of the specified dimension.
octave_idx_type numel() const
Number of elements in the array.
Definition Array-base.h:440
Definition Cell.h:41
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:92
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition dim-vector.h:341
void resize(int n, int fill_value=0)
Definition dim-vector.h:278
octave_idx_type ndims() const
Number of dimensions.
Definition dim-vector.h:263
void loadobj(std::vector< std::tuple< octave_map, uint32_t, bool > > &m, dim_vector &dv)
std::vector< std::tuple< octave_map, uint32_t, bool > > saveobj(std::vector< bool > &is_new)
string_vector fieldnames() const
Definition oct-map.h:332
const Cell & contents(const_iterator p) const
Definition oct-map.h:310
void assign(const std::string &k, const Cell &val)
Definition oct-map.h:344
static octave_map cat(int dim, octave_idx_type n, const octave_scalar_map *map_list)
Definition oct-map.cc:690
const octave_value & contents(const_iterator p) const
Definition oct-map.h:197
const_iterator end() const
Definition oct-map.h:185
bool contains(const std::string &name) const
Definition oct-map.h:216
const_iterator begin() const
Definition oct-map.h:184
void assign(const std::string &k, const octave_value &val)
Definition oct-map.h:230
std::string key(const_iterator p) const
Definition oct-map.h:192
bool is_classdef_object() const
Definition ov.h:653
bool is_uint32_type() const
Definition ov.h:722
std::string class_name() const
Definition ov.h:1360
octave_classdef * classdef_object_value(bool silent=false) const
Cell cell_value() const
bool is_scalar_type() const
Definition ov.h:742
octave_scalar_map scalar_map_value() const
bool is_defined() const
Definition ov.h:590
bool isempty() const
Definition ov.h:599
octave_uint32 uint32_scalar_value() const
Definition ov.h:951
bool is_uint8_type() const
Definition ov.h:716
bool iscell() const
Definition ov.h:602
octave_idx_type numel() const
Definition ov.h:557
octave_map map_value() const
uint8NDArray uint8_array_value() const
Definition ov.h:969
bool isstruct() const
Definition ov.h:647
uint32NDArray uint32_array_value() const
Definition ov.h:975
dim_vector dims() const
Definition ov.h:539
string_vector & append(const std::string &s)
Definition str-vec.cc:110
void resize(octave_idx_type n, const std::string &rfv="")
Definition str-vec.h:93
octave_idx_type numel() const
Definition str-vec.h:98
uint8NDArray create_filewrapper_metadata()
std::string get_class_name(const uint32_t class_id)
bool read_mat_subsystem(const octave_value &subsys, bool swap)
bool read_filewrapper(const Cell &fwrap_data, bool swap)
octave_map get_object_properties(const uint32_t obj_type_id, const uint32_t class_id, const bool saveobj_type, bool as_struct=false)
bool check_dyn_props(const uint32_t obj_dep_id)
uint32NDArray set_mcos_object_metadata(const octave_value &obj)
std::tuple< bool, uint32_t, uint32_t > get_object_dependencies(const uint32_t obj_id)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void warning(const char *fmt,...)
Definition error.cc:1083
void warning_with_id(const char *id, const char *fmt,...)
Definition error.cc:1098
void error(const char *fmt,...)
Definition error.cc:1008
octave_value load_mcos_object(const octave_value &objmetadata, bool as_struct)
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition oct-locbuf.h:44